From 4b231bbaf21939144ed8639d35f022834a406e59 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 17 Oct 2024 22:02:19 -0400
Subject: changed layout_isFlashcard to layout_flashcardType with value of
 'flashcard' to make it easy to search for flashcards. made carousel views
 able to focus() on a Doc.  added ability for carousel and card views to have
 anchors so they can be pinned and linked.  fixed pinning collections to save
 filters

---
 .../views/collections/CollectionCarouselView.tsx   | 51 +++++++++++++++++++++-
 1 file changed, 49 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 1f2bc908f..b043706fd 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,12 +3,17 @@ import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
+import { PinDocView } from '../PinFuncs';
 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';
@@ -27,6 +32,9 @@ export class CollectionCarouselView extends CollectionSubView() {
         makeObservable(this);
     }
 
+    componentDidMount(): void {
+        this._props.setContentViewBox?.(this);
+    }
     componentWillUnmount() {
         this._dropDisposer?.();
     }
@@ -67,6 +75,44 @@ 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);
+        return undefined;
+    };
+
+    getAnchor = (addAsAnnotation: boolean) => {
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
+        anchor.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);
+        }
+        return anchor;
+    };
+    // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
+    restoreView = (viewData: Doc) => {
+        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
+            this.layoutDoc._carousel_index = viewData.config_carousel_index;
+            return true;
+        }
+        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);
+    };
+
     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
         const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
@@ -113,6 +159,7 @@ export class CollectionCarouselView extends CollectionSubView() {
                 LayoutTemplateString={this._props.childLayoutString}
                 TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
                 childFilters={this.childDocFilters}
+                focus={this.focus}
                 hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
                 addDocument={this._props.addDocument}
                 ScreenToLocalTransform={this.contentScreenToLocalXf}
-- 
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/CollectionCarouselView.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 6d5b80adf8c0d1f93dce30401e1f90d11b838de3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Oct 2024 10:00:01 -0400
Subject: fixed following anchors to collection card/carousel to set the
 current/selected Doc

---
 src/client/documents/Documents.ts                  |  2 +-
 src/client/views/PinFuncs.ts                       |  6 +--
 src/client/views/ViewBoxInterface.ts               |  2 +-
 .../views/collections/CollectionCardDeckView.tsx   | 55 +++++++++-------------
 .../views/collections/CollectionCarousel3DView.tsx | 10 +---
 .../views/collections/CollectionCarouselView.tsx   | 10 +---
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          | 36 +++++++-------
 src/fields/ScriptField.ts                          |  6 +--
 10 files changed, 53 insertions(+), 78 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index be3b9c3bb..98e8bc932 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -373,7 +373,7 @@ export class DocumentOptions {
     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_card_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/PinFuncs.ts b/src/client/views/PinFuncs.ts
index 17964c656..bed473eec 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -16,7 +16,7 @@ export interface pinDataTypes {
     scrollable?: boolean;
     dataviz?: number[];
     pannable?: boolean;
-    type_collection?: boolean;
+    collectionType?: boolean;
     inkable?: boolean;
     filters?: boolean;
     pivot?: boolean;
@@ -65,7 +65,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
             pinProps.pinData.scrollable ||
             pinProps.pinData.temporal ||
             pinProps.pinData.pannable ||
-            pinProps.pinData.type_collection ||
+            pinProps.pinData.collectionType ||
             pinProps.pinData.clippable ||
             pinProps.pinData.datarange ||
             pinProps.pinData.dataview ||
@@ -118,7 +118,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
                     })
                 )
             );
-        if (pinProps.pinData.type_collection) pinDoc.config_type_collection = targetDoc._type_collection;
+        if (pinProps.pinData.collectionType) pinDoc.config_type_collection = targetDoc._type_collection;
         if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField) ?? new List<string>();
         if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
         if (pinProps.pinData.pannable) {
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index f66f6062e..b7980d74e 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -24,7 +24,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
     promoteCollection?: () => void; // moves contents of collection to parent
     updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
     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;
+    restoreView?: (viewSpec: Doc) => boolean; // DEPRECATED: do not use, it will go away.  see PresBox.restoreTargetDocView
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
     brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
     getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 729a1585a..beb45f326 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -51,14 +51,13 @@ export class CollectionCardView extends CollectionSubView() {
     private _textToDoc = new Map<string, Doc>();
     private _oldWheel: HTMLElement | null = null;
     private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
-    private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
+    private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
 
     @observable _forceChildXf = 0;
     @observable _hoveredNodeIndex = -1;
     @observable _docRefs = new ObservableMap<Doc, DocumentView>();
     @observable _maxRowCount = 10;
     @observable _docDraggedIndex: number = -1;
-    @observable _curDoc: Doc | undefined = undefined;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -165,10 +164,10 @@ export class CollectionCardView extends CollectionSubView() {
     /**
      * When in quiz mode, randomly selects a document
      */
-    quizMode = action(() => {
+    quizMode = () => {
         const randomIndex = Math.floor(Math.random() * this.childDocs.length);
-        this._curDoc = this.childDocs[randomIndex];
-    });
+        this.layoutDoc._card_curDoc = this.childDocs[randomIndex];
+    };
 
     setHoveredNodeIndex = action((index: number) => {
         if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
@@ -319,7 +318,7 @@ export class CollectionCardView extends CollectionSubView() {
         (doc: Doc) => () =>
             this._props.isContentActive?.() === false
                 ? false
-                : this._props.isDocumentActive?.() && this._curDoc === doc
+                : this._props.isDocumentActive?.() && this.curDoc() === doc
                   ? true
                   : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
                     ? false
@@ -347,7 +346,7 @@ export class CollectionCardView extends CollectionSubView() {
             scriptContext={this}
             focus={this.focus}
             onDoubleClickScript={this.onChildDoubleClick}
-            onClickScript={this._curDoc === doc ? undefined : this._clickScript}
+            onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript}
             dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
             dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
             showTags={BoolCast(this.layoutDoc.showChildTags)}
@@ -572,8 +571,8 @@ export class CollectionCardView extends CollectionSubView() {
      * @param doc doc that will be animated away from center focus
      */
     releaseCurDoc = action(() => {
-        const selDoc = this._curDoc;
-        this._curDoc = undefined;
+        const selDoc = this.curDoc();
+        this.layoutDoc._card_curDoc = undefined;
         const cardDocView = DocumentView.getDocumentView(selDoc, this.DocumentView?.());
         if (cardDocView && selDoc) {
             DocumentView.DeselectView(cardDocView);
@@ -590,7 +589,7 @@ export class CollectionCardView extends CollectionSubView() {
      * turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
      */
     cardPointerUp = action((doc: Doc) => {
-        if (this._curDoc === doc || this._dropped) {
+        if (this.curDoc() === doc || this._dropped) {
             this._dropped = false;
         } else {
             this.releaseCurDoc(); // NOTE: the onClick script for the card will select the new card (ie, 'doc')
@@ -601,28 +600,20 @@ export class CollectionCardView extends CollectionSubView() {
         const docs = DocListCast(this.Document[this.fieldKey]);
         if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
             const foundDoc = DocCast(
-                anchor.config_curDoc,
+                anchor.config_card_curDoc,
                 docs.find(doc => doc === DocCast(anchor.annotationOn, anchor))
             );
             options.didMove = foundDoc !== this.curDoc() ? true : false;
-            options.didMove && (this._curDoc = foundDoc);
+            options.didMove && (this.layoutDoc._card_curDoc = foundDoc);
         }
         return undefined;
     });
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_curDoc: this.curDoc() });
-        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() });
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         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
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_curDoc !== undefined && this.curDoc() !== viewData.config_curDoc) {
-            this._curDoc = DocCast(viewData.config_curDoc);
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
 
     /**
@@ -634,9 +625,9 @@ export class CollectionCardView extends CollectionSubView() {
         return this.sortedDocs.map((doc, index) => {
             const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
 
-            const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this._curDoc);
+            const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this.curDoc());
 
-            const translateToCenterIfActive = () => (doc === this._curDoc ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
+            const translateToCenterIfActive = () => (doc === this.curDoc() ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
 
             const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
             const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()), 
@@ -645,15 +636,15 @@ export class CollectionCardView extends CollectionSubView() {
             return (
                 <div
                     key={doc[Id]}
-                    className={`card-item${doc === this._curDoc ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+                    className={`card-item${doc === this.curDoc() ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
                     onPointerUp={() => this.cardPointerUp(doc)}
                     style={{
                         width: this.childPanelWidth(),
                         height: 'max-content',
-                        transform: `translateY(${this.adjustCardYtoFitArch(doc === this._curDoc, index)}px)
+                        transform: `translateY(${this.adjustCardYtoFitArch(doc === this.curDoc(), index)}px)
                                     translateX(calc(${translateToCenterIfActive()}% + ${this.horizontalAdjustmentForPartialRows(index, cardsInRow)}px))
-                                    rotate(${doc !== this._curDoc ? this.rotate(index) : 0}rad)
-                                    scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
+                                    rotate(${doc !== this.curDoc()? this.rotate(index) : 0}rad)
+                                    scale(${doc === this.curDoc()? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
                     }} // prettier-ignore
                     onPointerEnter={() => this.setHoveredNodeIndex(index)}
                     onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
@@ -671,10 +662,10 @@ export class CollectionCardView extends CollectionSubView() {
         isContentActive: emptyFunction,
         ScreenToLocalTransform: this.contentScreenToLocalXf,
     });
-    answered = action(() => {
-        this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
-    });
-    curDoc = () => this._curDoc;
+    answered = () => {
+        this.layoutDoc._card_curDoc = this.curDoc() ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+    };
+    curDoc = () => DocCast(this.layoutDoc._card_curDoc);
 
     render() {
         const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 081e2fe5a..61a74dd7a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -103,18 +103,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
     };
     getAnchor = (addAsAnnotation: boolean) => {
         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);
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         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
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
-            this.layoutDoc._carousel_index = viewData.config_carousel_index;
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index bfb58733d..f714e2a00 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -86,18 +86,10 @@ export class CollectionCarouselView extends CollectionSubView() {
 
     getAnchor = (addAsAnnotation: boolean) => {
         const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
-        PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
         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
-    restoreView = (viewData: Doc) => {
-        if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) {
-            this.layoutDoc._carousel_index = viewData.config_carousel_index;
-            return true;
-        }
-        return false;
-    };
     addDocTab = this.addLinkedDocTab;
 
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 5738e7ce3..d75c633ac 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -52,7 +52,7 @@ export class CollectionTimeView extends CollectionSubView() {
             title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as unknown as string, // title can take a functiono or a string
             annotationOn: this.Document,
         });
-        PinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
+        PinDocView(anchor, { pinData: { collectionType: true, pivot: 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
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0241fc2dd..ff711314b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1743,7 +1743,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         });
         PinDocView(
             anchor,
-            { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } },
+            { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, collectionType: true, filters: true } },
             this.Document
         );
 
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 212dfec08..5d3672aee 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { StopEvent, lightOrDark, returnFalse, returnOne, setupMoveUpEvents } fro
 import { emptyFunction, stringHash } from '../../../../Utils';
 import { Doc, DocListCast, Field, FieldResult, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
 import { Animation, DocData, TransitionTimer } from '../../../../fields/DocSymbols';
-import { Copy } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
 import { InkField } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
 import { ObjectField } from '../../../../fields/ObjectField';
@@ -382,7 +382,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (typeof res === 'string') {
                 const resObj = JSON.parse(res);
                 console.log('Parsed GPT Result ', resObj);
-                // eslint-disable-next-line no-restricted-syntax
                 for (const key in resObj) {
                     if (resObj[key]) {
                         console.log('typeof property', typeof resObj[key]);
@@ -563,11 +562,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const datarange = [DocumentType.FUNCPLOT].includes(targetType);
         const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG, DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
         const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
-        const typeCollection = targetType === DocumentType.COL;
+        const collectionType = targetType === DocumentType.COL;
         const filters = true;
         const pivot = true;
         const dataannos = false;
-        return { scrollable, pannable, inkable, type_collection: typeCollection, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+        return { scrollable, pannable, inkable, collectionType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
     }
 
     @action
@@ -575,7 +574,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         /* empty */
     };
     @action
-    // eslint-disable-next-line default-param-last
     static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: dataTypes, targetDoc?: Doc) {
         const bestTarget = bestTargetView?.Document ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
         if (!bestTarget) return undefined;
@@ -701,7 +699,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 changed = true;
             }
         }
-        if ((pinDataTypes?.type_collection && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
+        if ((pinDataTypes?.collectionType && activeItem.config_card_curDoc !== undefined) || (!pinDataTypes && activeItem.config_card_curDoc !== undefined)) {
+            if (bestTarget._card_curDoc !== activeItem.config_card_curDoc) {
+                bestTarget._card_curDoc = activeItem.config_card_curDoc;
+                changed = true;
+            }
+        }
+        if ((pinDataTypes?.collectionType && activeItem.config_carousel_index !== undefined) || (!pinDataTypes && activeItem.config_carousel_index !== undefined)) {
+            if (bestTarget._carousel_index !== activeItem.config_carousel_index) {
+                bestTarget._carousel_index = activeItem.config_carousel_index;
+                changed = true;
+            }
+        }
+        if ((pinDataTypes?.collectionType && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
             if (bestTarget._type_collection !== activeItem.config_type_collection) {
                 bestTarget._type_collection = activeItem.config_type_collection;
                 changed = true;
@@ -1135,7 +1145,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     return false;
                 }
             } else if (doc.type !== DocumentType.PRES) {
-                // eslint-disable-next-line operator-assignment
                 if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
                 doc.presentation_targetDoc = doc.createdFrom ?? doc; // dropped document will be a new embedding of an embedded document somewhere else.
                 doc.presentation_movement = PresMovement.Zoom;
@@ -1167,8 +1176,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
             if (curDoc && curDoc === this.activeItem)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         <b>
                             {index + 1}. {StrCast(curDoc.title)})
                         </b>
@@ -1176,15 +1184,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 );
             if (tagDoc)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         {index + 1}. {StrCast(curDoc.title)}
                     </div>
                 );
             if (curDoc)
                 return (
-                    // eslint-disable-next-line react/no-array-index-key
-                    <div key={index} className="selectedList-items">
+                    <div key={doc[Id]} className="selectedList-items">
                         {index + 1}. {StrCast(curDoc.title)}
                     </div>
                 );
@@ -1369,7 +1375,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const tagDoc = PresBox.targetRenderedDoc(doc);
             const srcContext = Cast(tagDoc.embedContainer, Doc, null);
             const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
-                // eslint-disable-next-line react/no-array-index-key
                 <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
                     <div className="pathOrder-frame">{index + 1}</div>
                 </div>
@@ -1620,7 +1625,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
         this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
         this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
-        // eslint-disable-next-line camelcase
         const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
         array.forEach(curDoc => {
             curDoc.presentation_transition = pt;
@@ -2018,7 +2022,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="presBox-effects">
                                     {this.generatedAnimations.map((elem, i) => (
                                         <div
-                                            // eslint-disable-next-line react/no-array-index-key
                                             key={i}
                                             className="presBox-effect-container"
                                             onClick={() => {
@@ -3046,7 +3049,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="Slide">
                         {mode !== CollectionViewType.Invalid ? (
                             <CollectionView
-                                // eslint-disable-next-line react/jsx-props-no-spreading
                                 {...this._props}
                                 PanelWidth={this._props.PanelWidth}
                                 PanelHeight={this.panelHeight}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 582c09f29..0252961d3 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -85,6 +85,7 @@ async function deserializeScript(scriptIn: ScriptField) {
 }
 
 @scriptingGlobal
+// eslint-disable-next-line no-use-before-define
 @Deserializable('script', (obj: unknown) => deserializeScript(obj as ScriptField))
 export class ScriptField extends ObjectField {
     @serializable
@@ -137,7 +138,6 @@ export class ScriptField extends ObjectField {
     [ToString]() {
         return this.script.originalScript;
     }
-    // eslint-disable-next-line default-param-last
     public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) {
         return CompileScript(script, {
             params: {
@@ -156,13 +156,11 @@ export class ScriptField extends ObjectField {
         });
     }
 
-    // eslint-disable-next-line default-param-last
     public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
         const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
     }
 
-    // eslint-disable-next-line default-param-last
     public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
         const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
         return compiled.compiled ? new ScriptField(compiled) : undefined;
@@ -186,6 +184,7 @@ export class ScriptField extends ObjectField {
 }
 
 @scriptingGlobal
+// eslint-disable-next-line no-use-before-define
 @Deserializable('computed', (obj: unknown) => deserializeScript(obj as ComputedField))
 export class ComputedField extends ScriptField {
     static undefined = '__undefined';
@@ -229,7 +228,6 @@ export class ComputedField extends ScriptField {
     [ToValue](doc: Doc)   { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore
     [Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
 
-    // eslint-disable-next-line default-param-last
     public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
         const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables });
         const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
-- 
cgit v1.2.3-70-g09d2

