From e17b1bdb09bfcadc717e687b09d2c18596341a10 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Feb 2023 21:15:58 -0500
Subject: fixed childLayoutString to work.  made images capable of fitWidth.
 fixed animating data field pres changes.  fixed lightbox to ignore
 annotations on collections.  fixed double-click on icon to open in lightbox.
 added options for turning off ink labels, and opening ink in lightbox. fixed
 closing ink strokes by dragging. fixed drawing ink to use coord sys of
 starting point and to render ink the correct width and to honor
 GestureOverlay mode properly. .

---
 src/client/views/nodes/MapBox/MapBox.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 95cb49037..5940fc075 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -542,8 +542,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // }
     };
 
-    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
-    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-- 
cgit v1.2.3-70-g09d2


From 32f5040c44dc302e3dd53cecd9be4cd51a474d3f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 14 Feb 2023 14:57:25 -0500
Subject: fixed pinning regions on pdf/web pages to allow proper pres
 following.  added click on pres item# to select w/o navigation.  fixed
 dashDocView to be selectable without losing selection to parent textbox. 
 added BingMaps rudimentary option to googleMaps

---
 package-lock.json                                  |  18 ++-
 package.json                                       |   2 +
 src/client/views/nodes/MapBox/MapBox.tsx           | 148 +++++++++++++--------
 src/client/views/nodes/PDFBox.tsx                  |   1 +
 src/client/views/nodes/WebBox.tsx                  |   1 +
 .../views/nodes/formattedText/DashDocView.tsx      |   5 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   3 +
 src/client/views/nodes/trails/PresBox.tsx          |  20 +--
 src/client/views/nodes/trails/PresElementBox.scss  |   6 +
 src/client/views/nodes/trails/PresElementBox.tsx   |  12 +-
 10 files changed, 143 insertions(+), 73 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 547a6e1d7..95a128419 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3675,6 +3675,11 @@
                 "file-uri-to-path": "1.0.0"
             }
         },
+        "bingmaps-react": {
+            "version": "1.2.10",
+            "resolved": "https://registry.npmjs.org/bingmaps-react/-/bingmaps-react-1.2.10.tgz",
+            "integrity": "sha512-fM887Sr6OIlo6ThmSpGfNEDssSytdnsMrbDTDs+YH/43etf0dlfcR4oaJvygy+fwn21hpP2lVvwTHUDZvGxZqA=="
+        },
         "bl": {
             "version": "4.1.0",
             "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -4970,9 +4975,9 @@
             }
         },
         "core-js": {
-            "version": "1.2.7",
-            "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
-            "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+            "version": "3.28.0",
+            "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.28.0.tgz",
+            "integrity": "sha512-GiZn9D4Z/rSYvTeg1ljAIsEqFm0LaN9gVtwDCrKL80zHtS31p9BAjmTxVqTQDMpwlMolJZOFntUG2uwyj7DAqw=="
         },
         "core-js-pure": {
             "version": "3.22.8",
@@ -8260,6 +8265,13 @@
                 "promise": "^7.1.1",
                 "setimmediate": "^1.0.5",
                 "ua-parser-js": "^0.7.30"
+            },
+            "dependencies": {
+                "core-js": {
+                    "version": "1.2.7",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+                    "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA=="
+                }
             }
         },
         "fd-slicer": {
diff --git a/package.json b/package.json
index 712354355..00ce356f9 100644
--- a/package.json
+++ b/package.json
@@ -165,6 +165,7 @@
         "bcrypt-nodejs": "0.0.3",
         "bezier-curve": "^1.0.0",
         "bezier-js": "^4.1.1",
+        "bingmaps-react": "^1.2.10",
         "bluebird": "^3.7.2",
         "body-parser": "^1.19.2",
         "bootstrap": "^4.6.1",
@@ -181,6 +182,7 @@
         "connect-mongo": "^2.0.3",
         "cookie-parser": "^1.4.6",
         "cookie-session": "^2.0.0",
+        "core-js": "^3.28.0",
         "cors": "^2.8.5",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5940fc075..039f11cd1 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,5 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+import BingMapsReact from 'bingmaps-react';
 import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -43,20 +44,22 @@ const mapContainerStyle = {
 };
 
 const defaultCenter = {
-    lat: 38.685,
-    lng: -115.234,
+    lat: 42.360081,
+    lng: -71.058884,
 };
 
 const mapOptions = {
     fullscreenControl: false,
 };
 
+const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
 const apiKey = process.env.GOOGLE_MAPS;
 
 const script = document.createElement('script');
 script.defer = true;
 script.async = true;
 script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+console.log(script.src);
 document.head.appendChild(script);
 
 /**
@@ -84,6 +87,7 @@ const options = {
 
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+    static UseBing = true;
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -551,69 +555,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     infoHeight = () => this.props.PanelHeight() / 5;
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
+
+    _bingSearchManager: any;
+    _bingMap: any;
+    get MicrosoftMaps() {
+        return (window as any).Microsoft.Maps;
+    }
+    // uses Bing Search to retrieve lat/lng for a location.  eg.,
+    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
+    // to move the map to that location:
+    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
+    //     this._bingMap.current.setView({
+    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+    //     });
+    //
+    bingGeocode = (map: any, query: string) => {
+        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
+            //If search manager is not defined, load the search module.
+            if (!this._bingSearchManager) {
+                //Create an instance of the search manager and call the geocodeQuery function again.
+                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
+                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
+                    res(this.bingGeocode(map, query));
+                });
+            } else {
+                this._bingSearchManager.geocode({
+                    where: query,
+                    callback: action((r: any) => {
+                        res(r.results[0].location);
+                    }),
+                    errorCallback: (e: any) => reject(),
+                });
+            }
+        });
+    };
+
+    bingViewOptions = {
+        center: { latitude: defaultCenter.lat, longitude: defaultCenter.lng },
+        mapTypeId: 'grayscale',
+    };
+    bingMapOptions = {
+        navigationBarMode: 'square',
+    };
+    bingMapReady = (map: any) => (this._bingMap = map.map);
     render() {
         const renderAnnotations = (docFilters?: () => string[]) => null;
-        // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
-        //      will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
-        //      freeform documents on the map does anything anyway, so getting rid of it for now.  Also, since documents
-        //      are rendered twice, adding a new note to the InfoWindow loses focus immediately on creation since it gets
-        //      shifted to the non-visible view of the document in this freeform view.
-        // <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-        //     renderDepth={this.props.renderDepth + 1}
-        //     isAnnotationOverlay={true}
-        //     fieldKey={this.annotationKey}
-        //     CollectionView={undefined}
-        //     setPreviewCursor={this.setPreviewCursor}
-        //     PanelWidth={this.panelWidth}
-        //     PanelHeight={this.panelHeight}
-        //     ScreenToLocalTransform={this.scrollXf}
-        //     scaling={returnOne}
-        //     dropAction={"alias"}
-        //     docFilters={docFilters || this.props.docFilters}
-        //     dontRenderDocuments={docFilters ? false : true}
-        //     select={emptyFunction}
-        //     bringToFront={emptyFunction}
-        //     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-        //     removeDocument={this.removeDocument}
-        //     moveDocument={this.moveDocument}
-        //     addDocument={this.sidebarAddDocument}
-        //     childPointerEvents={"all"}
-        //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
-                {/*console.log(apiKey)*/}
-                {/* <LoadScript
-                googleMapsApiKey={apiKey!}
-                libraries={['places', 'drawing']}
-            > */}
-                <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+                <div
+                    className="mapBox-wrapper"
+                    onWheel={e => e.stopPropagation()}
+                    onPointerDown={async e => {
+                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
+                        // just a simple test of bing maps geocode api
+                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
+                        // this._bingMap.current.setView({
+                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
+                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
+                        //     zoom: 15,
+                        // });
+                    }}
+                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                     {this.annotationLayer}
-                    <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
-                        <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
-                            <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
-                        </Autocomplete>
-
-                        {this.renderMarkers()}
-                        {this.allMapMarkers
-                            .filter(marker => marker.infoWindowOpen)
-                            .map(marker => (
-                                <MapBoxInfoWindow
-                                    key={marker[Id]}
-                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
-                                    place={marker}
-                                    markerMap={this.markerMap}
-                                    PanelWidth={this.infoWidth}
-                                    PanelHeight={this.infoHeight}
-                                    moveDocument={this.moveDocument}
-                                    isAnyChildContentActive={this.isAnyChildContentActive}
-                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                />
-                            ))}
-                        {/* {this.handleMapCenter(this._map)} */}
-                    </GoogleMap>
+
+                    {!MapBox.UseBing ? null : <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} />}
+                    <div style={{ display: MapBox.UseBing ? 'none' : undefined }}>
+                        <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+                            <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+                                <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+                            </Autocomplete>
+
+                            {this.renderMarkers()}
+                            {this.allMapMarkers
+                                .filter(marker => marker.infoWindowOpen)
+                                .map(marker => (
+                                    <MapBoxInfoWindow
+                                        key={marker[Id]}
+                                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                        place={marker}
+                                        markerMap={this.markerMap}
+                                        PanelWidth={this.infoWidth}
+                                        PanelHeight={this.infoHeight}
+                                        moveDocument={this.moveDocument}
+                                        isAnyChildContentActive={this.isAnyChildContentActive}
+                                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                    />
+                                ))}
+                            {/* {this.handleMapCenter(this._map)} */}
+                        </GoogleMap>
+                    </div>
                     {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                         <MarqueeAnnotator
                             rootDoc={this.rootDoc}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b88ac113e..5890c536d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -222,6 +222,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const anchor = Docs.Create.TextanchorDocument({
                 title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
                 unrendered: true,
+                annotationOn: this.rootDoc,
             });
             PresBox.pinDocView(anchor, { pinData: { scrollable: true, pannable: true } }, this.rootDoc);
             return anchor;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c4cca9679..c392f3577 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -322,6 +322,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true,
+                annotationOn: this.rootDoc,
             });
         anchor.text = ele?.textContent ?? '';
         anchor.textHtml = ele?.innerHTML;
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 722d0cc4f..19ee1fc7b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -12,6 +12,7 @@ import { Transform } from '../../../util/Transform';
 import { DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
+import { SelectionManager } from '../../../util/SelectionManager';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -192,10 +193,10 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     Document={this._finalLayout}
                     DataDoc={this._resolvedDataDoc}
                     addDocument={returnFalse}
-                    rootSelected={this._textBox.props.isSelected}
+                    rootSelected={returnFalse} //{this._textBox.props.isSelected}
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
-                    isContentActive={this._textBox.props.isContentActive}
+                    isContentActive={emptyFunction}
                     styleProvider={this._textBox.props.styleProvider}
                     docViewPath={this._textBox.props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 80b18b8b9..49acd8a57 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1487,6 +1487,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
             while (target && !target.dataset?.targethrefs) target = target.parentElement;
             FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
+            if (pcords && pcords.inside > 0 && this._editorView.state.doc.nodeAt(pcords.inside)?.type === this._editorView.state.schema.nodes.dashDoc) {
+                return;
+            }
         }
 
         if (e.button === 0 && this.props.isSelected(true) && !e.altKey) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0a96297b7..e07517113 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty
 import { AudioField } from '../../../../fields/URLField';
 import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
-import { Docs } from '../../../documents/Documents';
+import { Docs, DocumentOptions } from '../../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
@@ -869,13 +869,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         presDocView && SelectionManager.SelectView(presDocView, false);
     };
 
+    focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc);
+
     //Regular click
     @action
-    selectElement = async (doc: Doc) => {
+    selectElement = async (doc: Doc, noNav = false) => {
         CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.());
-        this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
-        // if (doc.presPinView) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0);
-        // else
+        !noNav && this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
         this.updateCurrentPresentation(DocCast(doc.context));
     };
 
@@ -911,19 +911,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     //regular click
     @action
-    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
+    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true, noNav = false) => {
         this.clearSelectedArray();
         this.addToSelectedArray(doc);
         this._eleArray.splice(0, this._eleArray.length, ref);
         this._dragArray.splice(0, this._dragArray.length, drag);
-        focus && this.selectElement(doc);
+        focus && this.selectElement(doc, noNav);
         selectPres && this.selectPres();
     };
 
-    modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
+    modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean, noNav: boolean = false) => {
         if (cmdClick) this.multiSelect(doc, ref, drag);
         else if (shiftClick) this.shiftSelect(doc, ref, drag);
-        else this.regularSelect(doc, ref, drag, focus);
+        else this.regularSelect(doc, ref, drag, focus, noNav);
     };
 
     static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e);
@@ -2253,7 +2253,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 filterAddDocument={this.addDocumentFilter}
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
-                                focus={this.selectElement}
+                                focus={this.focusElement}
                                 scriptContext={this}
                                 ScreenToLocalTransform={this.getTransform}
                                 AddToMap={this.AddToMap}
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 415253af1..4f95f0c1f 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -44,6 +44,12 @@ $slide-active: #5b9fdd;
     display: flex;
     align-items: center;
 
+    .presItem-number {
+        cursor: pointer;
+        &:hover {
+            background-color: $light-blue;
+        }
+    }
     .presItem-name {
         display: flex;
         min-width: 20px;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 788900b46..f265c1315 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -592,7 +592,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 width: `calc(100% ${this.rootDoc.presExpandInlineButton ? '- 50%' : ''} - ${this.presButtons.length * 22}px`,
                                 cursor: isSelected ? 'text' : 'grab',
                             }}>
-                            <div>{`${this.indexInPres + 1}.  `}</div>
+                            <div
+                                className="presItem-number"
+                                title="select without navigation"
+                                style={{ pointerEvents: this.presBoxView?.isContentActive() ? 'all' : undefined }}
+                                onPointerDown={e => {
+                                    e.stopPropagation();
+                                    if (this._itemRef.current && this._dragRef.current) {
+                                        this.presBoxView?.modifierSelect(activeItem, this._itemRef.current, this._dragRef.current, false, false, false, true);
+                                    }
+                                }}
+                                onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}.  `}</div>
                             <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
                         </div>
                         {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-- 
cgit v1.2.3-70-g09d2


From 9c63f3833f15cc995c2255b83923384686127f3e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 1 Mar 2023 11:57:05 -0500
Subject: changed inPlace link following to be lightbox.  allowed collections
 to be labeled as lightboxes.  lightbox collections display link targets in an
 overlay that hides the rest of their content

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/DocumentManager.ts                 | 28 ++++++----
 src/client/util/LinkFollower.ts                    | 10 ++--
 src/client/views/DocumentDecorations.tsx           | 13 ++---
 src/client/views/MainView.tsx                      |  1 -
 src/client/views/PropertiesButtons.tsx             | 62 +++++++++++-----------
 src/client/views/PropertiesView.tsx                |  1 -
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        | 22 ++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 src/client/views/nodes/DocumentView.tsx            | 17 +++---
 src/client/views/nodes/MapBox/MapBox.tsx           |  1 -
 src/fields/documentSchemas.ts                      |  4 +-
 13 files changed, 88 insertions(+), 78 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d2ab67849..70c57e8fa 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -160,6 +160,7 @@ export class DocumentOptions {
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target
     _showTitle?: string; // field name to display in header (:hover is an optional suffix)
+    _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target
     _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
     _scrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index d9273c2c9..0d10bed43 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,7 +10,7 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie
 import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentView, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
@@ -307,16 +307,22 @@ export class DocumentManager {
             }
             //}
             if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc);
-            const doFocus = (forceDidFocus: boolean) =>
-                focusView.focus(originalTarget, {
-                    ...options,
-                    originalTarget,
-                    afterFocus: (didFocus: boolean) =>
-                        new Promise<ViewAdjustment>(res => {
-                            focusAndFinish(forceDidFocus || didFocus);
-                            res(ViewAdjustment.doNothing);
-                        }),
-                });
+            const doFocus = (forceDidFocus: boolean) => {
+                if (options.openInLightbox || (!options.originatingDoc?.followLinkLocation && DocCast(focusView.rootDoc.context)?._isLightbox)) {
+                    focusView.props.addDocTab(targetDoc, OpenWhere.lightbox);
+                    focusAndFinish(true);
+                } else {
+                    focusView.focus(originalTarget, {
+                        ...options,
+                        originalTarget,
+                        afterFocus: (didFocus: boolean) =>
+                            new Promise<ViewAdjustment>(res => {
+                                focusAndFinish(forceDidFocus || didFocus);
+                                res(ViewAdjustment.doNothing);
+                            }),
+                    });
+                }
+            };
             if (focusView.props.Document.layoutKey === 'layout_icon' && focusView.rootDoc.type !== DocumentType.SCRIPTING) {
                 focusView.iconify(() => doFocus(true));
             } else {
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 50bc89ed2..e86e68c21 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -3,6 +3,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { List } from '../../fields/List';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
 import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { DocumentDecorations } from '../views/DocumentDecorations';
 import { LightboxView } from '../views/LightboxView';
 import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView';
@@ -36,7 +37,9 @@ export class LinkFollower {
             const createTabForTarget = (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => {
                     const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere);
-                    docViewProps.addDocTab(doc, where);
+                    const lightbox = where === OpenWhere.lightbox && DocumentManager.GetContextPath(doc).find(container => container.isLightbox && DocumentManager.Instance.getDocumentView(container));
+                    const addDocTab = lightbox ? DocumentManager.Instance.getDocumentView(lightbox)?.ComponentView?.addDocTab : undefined;
+                    (addDocTab ?? docViewProps.addDocTab)(doc, where);
                     setTimeout(() => {
                         const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
                         if (targDocView) {
@@ -51,7 +54,7 @@ export class LinkFollower {
                             });
                         } else {
                             finished?.();
-                            res(where !== OpenWhere.inPlace || BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace'  resetting the initial focus&zoom would negate the zoom into the target
+                            res(BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing);
                         }
                     }, 100);
                 });
@@ -109,6 +112,7 @@ export class LinkFollower {
                         zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkTransitionTime, 500),
                         zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null),
                         easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
+                        openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox,
                         effect: sourceDoc,
                         originatingDoc: sourceDoc,
                         zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText),
@@ -123,7 +127,7 @@ export class LinkFollower {
                     } else {
                         const containerDocContext = DocumentManager.GetContextPath(target);
                         const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext;
-                        DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished);
+                        DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.lightbox), finished), targetContexts, allFinished);
                     }
                 };
                 let movedTarget = false;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4e51f10a8..eeef01d17 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -27,7 +27,7 @@ import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
-import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
@@ -283,11 +283,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
                     Doc.deiconifyView(openDoc);
                 }
-                LightboxView.SetLightboxDoc(
-                    openDoc,
-                    undefined,
-                    selectedDocs.slice(1).map(view => view.props.Document)
-                );
+                selectedDocs[0].props.addDocTab(openDoc, OpenWhere.lightbox);
+                // LightboxView.SetLightboxDoc(
+                //     openDoc,
+                //     undefined,
+                //     selectedDocs.slice(1).map(view => view.props.Document)
+                // );
             }
         }
         SelectionManager.DeselectAll();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1e9220e1e..95c0f3755 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -692,7 +692,6 @@ export class MainView extends React.Component {
         if (doc.dockingConfig) return DashboardView.openDashboard(doc);
         // prettier-ignore
         switch (whereFields[0]) {
-            case OpenWhere.inPlace:  // fall through to lightbox
             case OpenWhere.lightbox:   return LightboxView.AddDocTab(doc, location);
             case OpenWhere.dashboard:  return DashboardView.openDashboard(doc);
             case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc);
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 7e33ee495..a5c01490f 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -7,7 +7,7 @@ import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
-import { BoolCast, StrCast } from '../../fields/Types';
+import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
 import { DocUtils } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
@@ -20,6 +20,7 @@ import { VideoBox } from './nodes/VideoBox';
 import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
 import React = require('react');
+import { LinkManager } from '../util/LinkManager';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -118,14 +119,14 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             on => 'object-group'
         );
     }
-    // this implments a container pattern by marking the targetDoc (collection) as an inPlace container,
-    // and then making the contained collection be a "menu" such that when any of its contents are clicked,
-    // they will open their targets in the outer container.  To get back to the "menu", you click on the main container.
-    @computed get inPlaceContainerButton() {
+    // this implments a container pattern by marking the targetDoc (collection) as a lightbox
+    // that always fits its contents to its container and that hides all other documents when
+    // a link is followed that targets a 'lightbox' destination
+    @computed get isLightboxButton() {
         return this.propertyToggleBtn(
-            'In Place',
-            'isInPlaceContainer',
-            on => `${on ? 'Make' : 'Remove'} in place container flag`,
+            'Lightbox',
+            'isLightbox',
+            on => `${on ? 'Set' : 'Remove'} lightbox flag`,
             on => 'window-restore',
             onClick => {
                 SelectionManager.Views().forEach(dv => {
@@ -134,16 +135,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     // containerDoc.noShadow =
                     // containerDoc.noHighlighting =
                     // containerDoc._forceActive =
-                    containerDoc._fitContentsToBox = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer;
-                    containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined;
+                    containerDoc._fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox;
+                    containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined;
                     const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]);
                     dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' });
                     containerContents.forEach(doc => {
-                        DocListCast(doc.links).forEach(link => {
-                            doc.isLinkButton = true;
-                            //doc.followLinkLocation = OpenWhere.inPlace;
-                            link.linkDisplay = false;
-                        });
+                        DocListCast(doc.links).forEach(link => (link.linkDisplay = false));
                     });
                 });
             }
@@ -290,7 +287,6 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @undoBatch
     @action
     handleOptionChange = (onClick: string) => {
-        this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick);
         SelectionManager.Views()
             .filter(dv => dv.docView)
             .map(dv => dv.docView!)
@@ -305,7 +301,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                         break;
                     case 'linkInPlace':
                         docView.toggleFollowLink(false, false);
-                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.inPlace : undefined;
+                        docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.lightbox : undefined;
                         break;
                     case 'linkOnRight':
                         docView.toggleFollowLink(false, false);
@@ -327,24 +323,26 @@ export class PropertiesButtons extends React.Component<{}, {}> {
             ['nothing', 'Select Document'],
             ['enterPortal', 'Enter Portal'],
             ['toggleDetail', 'Toggle Detail'],
-            ['linkInPlace', 'Open in Place'],
+            ['linkInPlace', 'Open Link in Lightbox'],
             ['linkOnRight', 'Open Link on Right'],
         ];
-        const currentSelection = this.selectedDoc.onClickBehavior;
-        // Get items to place into the list
-
         const list = buttonList.map(value => {
-            const click = () => {
-                this.handleOptionChange(value[0]);
-            };
+            const click = () => this.handleOptionChange(value[0]);
+            const linkButton = BoolCast(this.selectedDoc._isLinkButton);
+            const followLoc = this.selectedDoc._followLinkLocation;
+            const linkedToLightboxView = () => DocListCast(this.selectedDoc.links).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox);
+
+            let active = false;
+            // prettier-ignore
+            switch (value[0]) {
+                case 'linkInPlace': active = linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView();  break;
+                case 'linkOnRight': active = linkButton && followLoc === OpenWhere.addRight; break;
+                case 'enterPortal': active = linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break;
+                case 'toggleDetail':active = ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break;
+                case 'nothing':     active = !linkButton && this.selectedDoc.onClick === undefined;break;
+            }
             return (
-                <div
-                    className="list-item"
-                    key={`${value}`}
-                    style={{
-                        backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined,
-                    }}
-                    onClick={click}>
+                <div className="list-item" key={`${value}`} style={{ backgroundColor: active ? Colors.LIGHT_BLUE : undefined }} onClick={click}>
                     {value[1]}
                 </div>
             );
@@ -413,7 +411,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.freezeThumb)}
                 {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
-                {toggle(this.inPlaceContainerButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
+                {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
                 {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
                 {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e9d1433c3..0fe165783 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1626,7 +1626,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                                 <option value={OpenWhere.add}>Opening in new tab</option>
                                                 <option value={OpenWhere.replace}>Replacing current tab</option>
                                                 <option value={OpenWhere.inParent}>Opening in same collection</option>
-                                                <option value={OpenWhere.inPlace}>Opening in place</option>
                                                 {LinkManager.currentLink?.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
                                             </select>
                                         </div>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 90406ed3c..316f1e4e9 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -191,7 +191,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
                 optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' });
             }
-            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._isLightbox), icon: 'project-diagram' });
 
             if (!Doc.noviceMode && false) {
                 optionItems.push({
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 01872df84..042c2b71b 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -350,7 +350,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     //  replace[:{left,right,top,bottom,<any string>}] - e.g., "replace" will replace the current stack contents,
     //                                  "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
     //                                   "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
-    //  inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
+    //  lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox
     addDocTab = (doc: Doc, location: OpenWhere) => {
         SelectionManager.DeselectAll();
         const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':');
@@ -359,17 +359,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         // prettier-ignore
         switch (whereFields[0]) {
             case undefined:
-            case OpenWhere.inPlace:    {
-                if (this.layoutDoc?.isInPlaceContainer) {
-                    const inPlaceView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
-                    const data = inPlaceView?.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)];
-                    if (inPlaceView && (!data || data instanceof List)) {
-                        inPlaceView.layoutDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List<Doc>([doc]);
-                        return true;
-                    }
-                }
-            }
-            case OpenWhere.lightbox:   return LightboxView.AddDocTab(doc, location);
+            case OpenWhere.lightbox:   if (this.layoutDoc?._isLightbox) {
+                                            const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined;
+                                            const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)];
+                                            if (lightboxView && (!data || data instanceof List)) {
+                                                lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]);
+                                                return true;
+                                            }
+                                       }
+                                       return LightboxView.AddDocTab(doc, location);
             case OpenWhere.dashboard:  return DashboardView.openDashboard(doc);
             case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc);
             case OpenWhere.close:      return CollectionDockingView.CloseSplit(doc, whereMods);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 78804b070..be8a72369 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1414,8 +1414,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     false
                 );
             case undefined:
-            case OpenWhere.inPlace:
-                if (this.layoutDoc.isInPlaceContainer) {
+            case OpenWhere.lightbox:
+                if (this.layoutDoc._isLightbox) {
                     this.layoutDoc[this.props.fieldKey] = new List<Doc>(doc instanceof Doc ? [doc] : doc);
                     return true;
                 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index beb0f8e3a..8257c9c49 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -72,7 +72,6 @@ export enum ViewAdjustment {
 }
 
 export enum OpenWhere {
-    inPlace = 'inPlace',
     lightbox = 'lightbox',
     add = 'add',
     addLeft = 'add:left',
@@ -109,6 +108,7 @@ export interface DocFocusOptions {
     effect?: Doc; // animation effect for focus
     noSelect?: boolean; // whether target should be selected after focusing
     playAudio?: boolean; // whether to play audio annotation on focus
+    openInLightbox?: boolean; // whether to open target in lightbox or just focus on it
     zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
     toggleTarget?: boolean; // whether to toggle target on and off
     originatingDoc?: Doc; // document that triggered the focus
@@ -123,6 +123,7 @@ export interface DocComponentView {
     scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
     scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
     brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
+    addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void; // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
@@ -640,7 +641,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                         );
                     UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
                 } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) {
-                    UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
+                    //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap');
+                    UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
                 }
@@ -870,13 +872,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @undoBatch
     @action
-    makeIntoPortal = async () => {
+    makeIntoPortal = () => {
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
-            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' });
-            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from');
+            DocUtils.MakeLink(
+                { doc: this.props.Document },
+                { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) },
+                'portal to:portal from'
+            );
         }
-        this.Document.followLinkLocation = OpenWhere.inPlace;
+        this.Document.followLinkLocation = OpenWhere.lightbox;
         this.Document.followLinkZoom = true;
         this.Document._isLinkButton = true;
     };
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 039f11cd1..c182dfd19 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -331,7 +331,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
                 const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
-                doc.onClickBehavior = 'enterPortal';
                 if (existingMarker) {
                     Doc.AddDocToList(existingMarker, 'data', doc);
                 } else {
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 10324449f..5b489a96c 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -84,11 +84,11 @@ export const documentSchema = createSchema({
     onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
     onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop)
     onDragStart: ScriptField, // script to run when document is dragged (without being selected).  the script should return the Doc to be dropped.
-    followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, inPlace, default, )
+    followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, )
     hideLinkButton: 'boolean', // whether the blue link counter button should be hidden
     hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden
     linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints.
-    isInPlaceContainer: 'boolean', // whether the marked object will display addDocTab() calls that target "inPlace" destinations
+    isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations
     isLinkButton: 'boolean', // whether document functions as a link follow button to follow the first link on the document when clicked
     layers: listSpec('string'), // which layers the document is part of
     _lockedPosition: 'boolean', // whether the document can be moved (dragged)
-- 
cgit v1.2.3-70-g09d2

