From 82a74549a6b1c942fabaf8396c450b70d65e81dd Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Mar 2024 23:28:11 -0400
Subject: fixed following link to pdf text selection with highlights. fixed
 hide key/value in dashfieldView. fixed selecting dashFieldView nodes.

---
 src/client/views/nodes/MapBox/MapBox.tsx | 791 ++++++++-----------------------
 1 file changed, 191 insertions(+), 600 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 927e6fad4..36dad2747 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -6,7 +6,7 @@ import { IconButton, Size, Type } from 'browndash-components';
 import * as d3 from 'd3';
 import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson';
 import mapboxgl, { LngLat, LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl';
-import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { CirclePicker, ColorResult } from 'react-color';
@@ -14,7 +14,6 @@ import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState
 import { MarkerEvent } from 'react-map-gl/dist/esm/types';
 import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils';
 import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
-import { DocCss, Highlight } from '../../../../fields/DocSymbols';
 import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { DocUtils, Docs } from '../../../documents/Documents';
@@ -28,7 +27,7 @@ import { SidebarAnnos } from '../../SidebarAnnos';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { Colors } from '../../global/globalEnums';
 import { DocumentView } from '../DocumentView';
-import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
+import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
 import { PinProps, PresBox } from '../trails';
 import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons';
@@ -53,7 +52,6 @@ import { MarkerIcons } from './MarkerIcons';
  * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
  */
 
-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 MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNscHgwNDd1MDA3MXIydm92ODdianp6cGYifQ.WFAqbhwxtMHOWSPtu0l2uQ';
 const MAPBOX_FORWARD_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/';
 
@@ -66,72 +64,69 @@ type PopupInfo = {
     description: string;
 };
 
-// export type GeocoderControlProps = Omit<GeocoderOptions, 'accessToken' | 'mapboxgl' | 'marker'> & {
-//     mapboxAccessToken: string;
-//     marker?: Omit<MarkerProps, 'longitude' | 'latitude'>;
-//     position: ControlPosition;
-
-//     onResult: (...args: any[]) => void;
-// };
-
-type MapMarker = {
-    longitude: number;
-    latitude: number;
-};
-
-/**
- * Consider integrating later: allows for drawing, circling, making shapes on map
- */
-// const drawingManager = new window.google.maps.drawing.DrawingManager({
-//     drawingControl: true,
-//     drawingControlOptions: {
-//         position: google.maps.ControlPosition.TOP_RIGHT,
-//         drawingModes: [
-//             google.maps.drawing.OverlayType.MARKER,
-//             // currently we are not supporting the following drawing mode on map, a thought for future development
-//             google.maps.drawing.OverlayType.CIRCLE,
-//             google.maps.drawing.OverlayType.POLYLINE,
-//         ],
-//     },
-// });
-
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
     public static LayoutString(fieldKey: string) {
         return FieldView.LayoutString(MapBox, fieldKey);
     }
-    private _dragRef = React.createRef<HTMLDivElement>();
+    private _unmounting = false;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _mapRef: React.RefObject<MapRef> = React.createRef();
     private _disposers: { [key: string]: IReactionDisposer } = {};
-    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
 
     constructor(props: FieldViewProps) {
         super(props);
         makeObservable(this);
     }
 
-    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @computed get allSidebarDocs() {
-        return DocListCast(this.dataDoc[this.SidebarKey]);
-    }
+    @observable _featuresFromGeocodeResults: any[] = [];
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @observable _selectedPinOrRoute: Doc | undefined = undefined; // The pin that is selected
+    @observable _mapReady = false;
+    @observable _isAnimating: boolean = false;
+    @observable _routeToAnimate: Doc | undefined = undefined;
+    @observable _animationPhase: number = 0;
+    @observable _finishedFlyTo: boolean = false;
+    @observable _frameId: number | null = null;
+    @observable _animationUtility: AnimationUtility | null = null;
+    @observable _settingsOpen: boolean = false;
+    @observable _mapStyle: string = 'mapbox://styles/mapbox/standard';
+    @observable _showTerrain: boolean = true;
+    @observable _currentPopup: PopupInfo | undefined = undefined;
+    @observable _isStreetViewAnimation: boolean = false;
+    @observable _animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM;
+    @observable _animationLineColor: string = '#ffff00';
+    @observable _temporaryRouteSource: FeatureCollection = { type: 'FeatureCollection', features: [] };
+    @observable _dynamicRouteFeature: Feature<Geometry, GeoJsonProperties> = {
+        type: 'Feature',
+        properties: {},
+        geometry: { type: 'LineString', coordinates: [] },
+    };
+
+    @observable path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = {
+        type: 'Feature',
+        geometry: { type: 'LineString', coordinates: [] },
+        properties: {},
+    };
+
     // this list contains pushpins and configs
-    @computed get allAnnotations() {
-        return DocListCast(this.dataDoc[this.annotationKey]);
-    }
-    @computed get allPushpins() {
-        return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN);
-    }
-    @computed get allRoutes() {
-        return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE);
+    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } //prettier-ignore
+    @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } //prettier-ignore
+    @computed get allPushpins() { return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN); } //prettier-ignore
+    @computed get allRoutes() { return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE); } //prettier-ignore
+    @computed get SidebarShown() { return this.layoutDoc._layout_showSidebar ? true : false; } //prettier-ignore
+    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } //prettier-ignore
+    @computed get SidebarKey() {  return this.fieldKey + '_sidebar'; } //prettier-ignore
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4'));
     }
     @computed get updatedRouteCoordinates(): Feature<Geometry, GeoJsonProperties> {
-        if (this.routeToAnimate?.routeCoordinates) {
-            const originalCoordinates: Position[] = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
+        if (this._routeToAnimate?.routeCoordinates) {
+            const originalCoordinates: Position[] = JSON.parse(StrCast(this._routeToAnimate.routeCoordinates));
             // const index = Math.floor(this.animationPhase * originalCoordinates.length);
-            const index = this.animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
-            console.log('Animation phase', this.animationPhase);
+            const index = this._animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
+            console.log('Animation phase', this._animationPhase);
             const startIndex = Math.floor(index);
             const endIndex = Math.ceil(index);
             let feature: Feature<Geometry, GeoJsonProperties>;
@@ -147,7 +142,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 feature = {
                     type: 'Feature',
                     properties: {
-                        routeTitle: StrCast(this.routeToAnimate.title),
+                        routeTitle: StrCast(this._routeToAnimate.title),
                     },
                     geometry: geometry,
                 };
@@ -158,9 +153,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 const fraction = index - startIndex;
 
                 const interpolator = d3.interpolateArray(startCoord, endCoord);
-
                 const interpolatedCoord = interpolator(fraction);
-
                 const coordinates = originalCoordinates.slice(0, startIndex + 1).concat([interpolatedCoord]);
 
                 geometry = {
@@ -170,14 +163,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 feature = {
                     type: 'Feature',
                     properties: {
-                        routeTitle: StrCast(this.routeToAnimate.title),
+                        routeTitle: StrCast(this._routeToAnimate.title),
                     },
                     geometry: geometry,
                 };
             }
 
             autorun(() => {
-                const animationUtil = this.animationUtility;
+                const animationUtil = this._animationUtility;
                 const concattedCoordinates = geometry.coordinates.concat(originalCoordinates.slice(endIndex));
                 const newFeature: Feature<LineString, turf.Properties> = {
                     type: 'Feature',
@@ -204,11 +197,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         };
     }
     @computed get selectedRouteCoordinates(): Position[] {
-        let coordinates: Position[] = [];
-        if (this.routeToAnimate?.routeCoordinates) {
-            coordinates = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates));
-        }
-        return coordinates;
+        return !this._routeToAnimate?.routeCoordinates ? [] : JSON.parse(StrCast(this._routeToAnimate.routeCoordinates));
     }
 
     @computed get allRoutesGeoJson(): FeatureCollection {
@@ -233,29 +222,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         };
     }
 
-    @computed get SidebarShown() {
-        return this.layoutDoc._layout_showSidebar ? true : false;
-    }
-    @computed get sidebarWidthPercent() {
-        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
-    }
-    @computed get sidebarColor() {
-        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4'));
-    }
-    @computed get SidebarKey() {
-        return this.fieldKey + '_sidebar';
-    }
-
     componentDidMount() {
         this._unmounting = false;
         this._props.setContentViewBox?.(this);
     }
 
-    _unmounting = false;
-    componentWillUnmount(): void {
+    componentWillUnmount() {
         this._unmounting = true;
         this.deselectPinOrRoute();
-        this._rerenderTimeout && clearTimeout(this._rerenderTimeout);
         Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
     }
 
@@ -269,7 +243,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
-            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPinOrRoute;
+            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this._selectedPinOrRoute;
             if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
                 existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
             }
@@ -370,10 +344,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
         const sourceAnchorCreator = action(() => {
             const note = this.getAnchor(true);
-            if (note && this.selectedPinOrRoute) {
-                note.latitude = this.selectedPinOrRoute.latitude;
-                note.longitude = this.selectedPinOrRoute.longitude;
-                note.map = this.selectedPinOrRoute.map;
+            if (note && this._selectedPinOrRoute) {
+                note.latitude = this._selectedPinOrRoute.latitude;
+                note.longitude = this._selectedPinOrRoute.longitude;
+                note.map = this._selectedPinOrRoute.map;
             }
             return note as Doc;
         });
@@ -399,10 +373,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         const createFunc = undoable(
             action(() => {
                 const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]);
-                if (note && this.selectedPinOrRoute) {
-                    note.latitude = this.selectedPinOrRoute.latitude;
-                    note.longitude = this.selectedPinOrRoute.longitude;
-                    note.map = this.selectedPinOrRoute.map;
+                if (note && this._selectedPinOrRoute) {
+                    note.latitude = this._selectedPinOrRoute.latitude;
+                    note.longitude = this._selectedPinOrRoute.longitude;
+                    note.map = this._selectedPinOrRoute.map;
                 }
             }),
             'create note annotation'
@@ -423,12 +397,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         return false;
     };
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
-
-    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
-
     pointerEvents = () => (this._props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
-
     panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1) - this.sidebarWidth();
     panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
     scrollXf = () => this.ScreenToLocalBoxXf().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
@@ -439,52 +408,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
     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(),
-                });
-            }
-        });
-    };
-
-    @observable
-    bingSearchBarContents: any = this.Document.map; // For Bing Maps: The contents of the Bing search bar (string)
-
-    geoDataRequestOptions = {
-        entityType: 'PopulatedPlace',
-    };
-
-    // The pin that is selected
-    @observable selectedPinOrRoute: Doc | undefined = undefined;
-
     @action
     deselectPinOrRoute = () => {
-        if (this.selectedPinOrRoute) {
+        if (this._selectedPinOrRoute) {
             // // Removes filter
             // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
             // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
@@ -511,79 +437,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         }
         return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
     };
-    /*
-     * Pushpin onclick
-     */
-    @action
-    pushpinClicked = (pinDoc: Doc) => {
-        this.deselectPinOrRoute();
-        this.selectedPinOrRoute = pinDoc;
-        this.bingSearchBarContents = pinDoc.map;
-
-        // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match');
-        // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
-
-        this.recolorPin(this.selectedPinOrRoute, 'green');
-
-        MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
-        MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
-        MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
-        MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
-
-        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPinOrRoute.latitude, this.selectedPinOrRoute.longitude));
-        const x = point.x + (this._props.PanelWidth() - this.sidebarWidth()) / 2;
-        const y = point.y + this._props.PanelHeight() / 2 + 32;
-        const cpt = this.ScreenToLocalBoxXf().inverse().transformPoint(x, y);
-        MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);
-
-        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
-    };
-
-    /**
-     * Map OnClick
-     */
-    @action
-    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
-        this._props.select(false);
-        this.deselectPinOrRoute();
-    };
-    /*
-     * Updates values of layout doc to match the current map
-     */
-    @action
-    mapRecentered = () => {
-        if (
-            Math.abs(NumCast(this.dataDoc.latitude) - this._bingMap.current.getCenter().latitude) > 1e-7 || //
-            Math.abs(NumCast(this.dataDoc.longitude) - this._bingMap.current.getCenter().longitude) > 1e-7
-        ) {
-            this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
-            this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
-            this.dataDoc.map = '';
-            this.bingSearchBarContents = '';
-        }
-        this.dataDoc.map_zoom = this._bingMap.current.getZoom();
-    };
-    /*
-     * Updates maptype
-     */
-    @action
-    updateMapType = () => (this.dataDoc.map_type = this._bingMap.current.getMapTypeId());
-
-    /*
-     * For Bing Maps
-     * Called by search button's onClick
-     * Finds the geocode of the searched contents and sets location to that location
-     **/
-    @action
-    bingSearch = () => {
-        return this.bingGeocode(this._bingMap, this.bingSearchBarContents).then(location => {
-            this.dataDoc.latitude = location.latitude;
-            this.dataDoc.longitude = location.longitude;
-            this.dataDoc.map_zoom = this._bingMap.current.getZoom();
-            this.dataDoc.map = this.bingSearchBarContents;
-        });
-    };
 
     /*
      * Returns doc w/ relevant info
@@ -592,14 +445,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.Document.title,
-            text: (StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any,
-            config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
-            config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
+            text: (StrCast(this._selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any,
+            config_latitude: NumCast((existingPin ?? this._selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
+            config_longitude: NumCast((existingPin ?? this._selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
             // config_map_type: StrCast(this.dataDoc.map_type),
-            config_map: StrCast((existingPin ?? this.selectedPinOrRoute)?.map) || StrCast(this.dataDoc.map),
+            config_map: StrCast((existingPin ?? this._selectedPinOrRoute)?.map) || StrCast(this.dataDoc.map),
             layout_unrendered: true,
-            mapPin: existingPin ?? this.selectedPinOrRoute,
+            mapPin: existingPin ?? this._selectedPinOrRoute,
             annotationOn: this.Document,
         });
         if (anchor) {
@@ -613,25 +466,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     map_docToPinMap = new Map<Doc, any>();
     map_pinHighlighted = new Map<Doc, boolean>();
-    /*
-     *   Input: pin doc
-     *   Adds MicrosoftMaps Pushpin to the map (render)
-     */
-    @action
-    addPushpin = (pin: Doc) => {
-        const pushPin = pin.infoWindowOpen
-            ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {})
-            : new this.MicrosoftMaps.Pushpin(
-                  new this.MicrosoftMaps.Location(pin.latitude, pin.longitude)
-                  // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
-              );
-
-        this._bingMap.current.entities.push(pushPin);
-
-        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
-        // this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
-        this.map_docToPinMap.set(pin, pushPin);
-    };
 
     /*
      *   Input: pin doc
@@ -640,27 +474,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
     @action
     removePushpinOrRoute = (pinOrRouteDoc: Doc) => this.removeMapDocument(pinOrRouteDoc, this.annotationKey);
 
-    /*
-     * Removes pushpin from map render
-     */
-    deletePushpin = (pinDoc: Doc) => {
-        if (!this._unmounting) {
-            this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
-        }
-        this.map_docToPinMap.delete(pinDoc);
-        this.selectedPinOrRoute = undefined;
-    };
-
     @action
     deleteSelectedPinOrRoute = undoable(() => {
         console.log('deleting');
-        if (this.selectedPinOrRoute) {
+        if (this._selectedPinOrRoute) {
             // Removes filter
-            Doc.setDocFilter(this.Document, 'latitude', this.selectedPinOrRoute.latitude, 'remove');
-            Doc.setDocFilter(this.Document, 'longitude', this.selectedPinOrRoute.longitude, 'remove');
-            Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPinOrRoute))}`, 'remove');
+            Doc.setDocFilter(this.Document, 'latitude', this._selectedPinOrRoute.latitude, 'remove');
+            Doc.setDocFilter(this.Document, 'longitude', this._selectedPinOrRoute.longitude, 'remove');
+            Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this._selectedPinOrRoute))}`, 'remove');
 
-            this.removePushpinOrRoute(this.selectedPinOrRoute);
+            this.removePushpinOrRoute(this._selectedPinOrRoute);
         }
         MapAnchorMenu.Instance.fadeOut(true);
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
@@ -677,7 +500,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         e.preventDefault();
         MapAnchorMenu.Instance.fadeOut(true);
         runInAction(() => {
-            this.temporaryRouteSource = {
+            this._temporaryRouteSource = {
                 type: 'FeatureCollection',
                 features: [],
             };
@@ -688,9 +511,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @action
     centerOnSelectedPin = () => {
-        if (this.selectedPinOrRoute) {
+        if (this._selectedPinOrRoute) {
             this._mapRef.current?.flyTo({
-                center: [NumCast(this.selectedPinOrRoute.longitude), NumCast(this.selectedPinOrRoute.latitude)],
+                center: [NumCast(this._selectedPinOrRoute.longitude), NumCast(this._selectedPinOrRoute.latitude)],
             });
         }
         // if (this.selectedPin) {
@@ -703,33 +526,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu);
     };
 
-    /**
-     * View options for bing maps
-     */
-    bingViewOptions = {
-        // center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng },
-        zoom: this.dataDoc.latitude ?? 10,
-        mapTypeId: 'grayscale',
-    };
-
-    /**
-     * Map options
-     */
-    bingMapOptions = {
-        navigationBarMode: 'square',
-        backgroundColor: '#f1f3f4',
-        enableInertia: true,
-        supportedMapTypes: ['grayscale', 'canvasLight'],
-        disableMapTypeSelectorMouseOver: true,
-        // showScalebar:true
-        // disableRoadView:true,
-        // disableBirdseye:true
-        streetsideOptions: {
-            showProblemReporting: false,
-            showCurrentAddress: false,
-        },
-    };
-
     recolorPin = (pin: Doc, color?: string) => {
         // this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin));
         // this.map_docToPinMap.delete(pin);
@@ -739,109 +535,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         // this.map_docToPinMap.set(pin, newpin);
     };
 
-    /*
-     * Called when BingMap is first rendered
-     * Initializes starting values
-     */
-    @observable _mapReady = false;
-    @action
-    bingMapReady = (map: any) => {
-        this._mapReady = true;
-        this._bingMap = map.map;
-        if (!this._bingMap.current) {
-            alert('NO Map!?');
-        }
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.mapRecentered, 'Map Layout Change'));
-        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
-
-        this._disposers.mapLocation = reaction(
-            () => this.Document.map,
-            mapLoc => (this.bingSearchBarContents = mapLoc),
-            { fireImmediately: true }
-        );
-        this._disposers.highlight = reaction(
-            () => this.allAnnotations.map(doc => doc[Highlight]),
-            () => {
-                const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
-                allConfigPins.forEach(({ doc, pushpin }) => {
-                    if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
-                        this.recolorPin(pushpin);
-                        this.map_pinHighlighted.delete(pushpin);
-                    }
-                });
-                allConfigPins.forEach(({ doc, pushpin }) => {
-                    if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
-                        this.recolorPin(pushpin, 'orange');
-                        this.map_pinHighlighted.set(pushpin, true);
-                    }
-                });
-            },
-            { fireImmediately: true }
-        );
-
-        this._disposers.location = reaction(
-            () => ({ lat: this.Document.latitude, lng: this.Document.longitude, zoom: this.Document.map_zoom, mapType: this.Document.map_type }),
-            locationObject => {
-                // if (this._bingMap.current)
-                try {
-                    locationObject?.zoom &&
-                        this._bingMap.current?.setView({
-                            mapTypeId: locationObject.mapType,
-                            zoom: locationObject.zoom,
-                            center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
-                        });
-                } catch (e) {
-                    console.log(e);
-                }
-            },
-            { fireImmediately: true }
-        );
-    };
-
-    dragToggle = (e: React.PointerEvent) => {
-        let dragClone: HTMLDivElement | undefined;
-
-        setupMoveUpEvents(
-            e,
-            e,
-            e => {
-                // move event
-                if (!dragClone) {
-                    dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; // copy draggable pin
-                    dragClone.style.position = 'absolute';
-                    dragClone.style.zIndex = '10000';
-                    DragManager.Root().appendChild(dragClone); // add clone to root
-                }
-                dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`;
-                return false;
-            },
-            e => {
-                // up event
-                if (!dragClone) return;
-                DragManager.Root().removeChild(dragClone);
-                let target = document.elementFromPoint(e.x, e.y); // element for specified x and y coordinates
-                while (target) {
-                    if (target === this._ref.current) {
-                        const cpt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
-                        const x = cpt[0] - (this._props.PanelWidth() - this.sidebarWidth()) / 2;
-                        const y = cpt[1] - 20 /* height of search bar */ - this._props.PanelHeight() / 2;
-                        const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
-                        this.createPushpin(location.latitude, location.longitude);
-                        break;
-                    }
-                    target = target.parentElement;
-                }
-            },
-            e => {
-                const createPin = () => this.createPushpin(this.Document.latitude, this.Document.longitude, this.Document.map);
-                if (this.bingSearchBarContents) {
-                    this.bingSearch().then(createPin);
-                } else createPin();
-            }
-        );
-    };
-
     // incrementer: number = 0;
     /*
      * Creates Pushpin doc and adds it to the list of annotations
@@ -880,7 +573,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
             if (createPinForDestination) {
                 this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
             }
-            this.temporaryRouteSource = {
+            this._temporaryRouteSource = {
                 type: 'FeatureCollection',
                 features: [],
             };
@@ -890,10 +583,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         // TODO: Display error that can't create route to same location
     }, 'createmaproute');
 
-    searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();
-
-    @observable
-    featuresFromGeocodeResults: any[] = [];
+    @action
+    searchbarKeyDown = (e: any) => {
+        if (e.key === 'Enter' && this._featuresFromGeocodeResults) {
+            const center = this._featuresFromGeocodeResults[0]?.center;
+            this._featuresFromGeocodeResults = [];
+            setTimeout(() => center && this._mapRef.current?.flyTo({ center }));
+        }
+    };
 
     @action
     addMarkerForFeature = (feature: any) => {
@@ -910,7 +607,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                     center: feature.center,
                 });
             }
-            this.featuresFromGeocodeResults = [];
+            this._featuresFromGeocodeResults = [];
         } else {
             // TODO: handle error
         }
@@ -922,11 +619,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
      */
     handleSearchChange = async (searchText: string) => {
         const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
-        if (features && !this.isAnimating) {
+        if (features && !this._isAnimating) {
             runInAction(() => {
-                this.settingsOpen = false;
-                this.featuresFromGeocodeResults = features;
-                this.routeToAnimate = undefined;
+                this._settingsOpen = false;
+                this._featuresFromGeocodeResults = features;
+                this._routeToAnimate = undefined;
             });
         }
         // try {
@@ -946,8 +643,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @action
     handleMapClick = (e: MapLayerMouseEvent) => {
-        this.featuresFromGeocodeResults = [];
-        this.settingsOpen = false;
+        this._featuresFromGeocodeResults = [];
+        this._settingsOpen = false;
         if (this._mapRef.current) {
             const features = this._mapRef.current.queryRenderedFeatures(e.point, {
                 layers: ['map-routes-layer'],
@@ -960,8 +657,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 const routeDoc: Doc | undefined = this.allRoutes.find(routeDoc => routeDoc.title === routeTitle);
                 this.deselectPinOrRoute(); // TODO: Also deselect route if selected
                 if (routeDoc) {
-                    this.selectedPinOrRoute = routeDoc;
-                    Doc.setDocFilter(this.Document, LinkedTo, `mapRoute=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
+                    this._selectedPinOrRoute = routeDoc;
+                    Doc.setDocFilter(this.Document, LinkedTo, `mapRoute=${Field.toScriptString(this._selectedPinOrRoute)}`, 'check');
 
                     // TODO: Recolor route
 
@@ -1008,7 +705,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         const features = await MapboxApiUtility.reverseGeocodeForFeatures(longitude, latitude);
         if (features) {
             runInAction(() => {
-                this.featuresFromGeocodeResults = features;
+                this._featuresFromGeocodeResults = features;
             });
         }
 
@@ -1028,21 +725,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         // }
     };
 
-    @observable
-    currentPopup: PopupInfo | undefined = undefined;
-
     @action
     handleMarkerClick = (e: MarkerEvent<mapboxgl.Marker, MouseEvent>, pinDoc: Doc) => {
-        this.featuresFromGeocodeResults = [];
+        this._featuresFromGeocodeResults = [];
         this.deselectPinOrRoute(); // TODO: check this method
-        this.selectedPinOrRoute = pinDoc;
+        this._selectedPinOrRoute = pinDoc;
         // this.bingSearchBarContents = pinDoc.map;
 
         // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match');
         // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check');
+        Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(this._selectedPinOrRoute)}`, 'check');
 
-        this.recolorPin(this.selectedPinOrRoute, 'green'); // TODO: check this method
+        this.recolorPin(this._selectedPinOrRoute, 'green'); // TODO: check this method
 
         MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute;
         MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
@@ -1072,12 +766,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         // })
     };
 
-    @observable
-    temporaryRouteSource: FeatureCollection = {
-        type: 'FeatureCollection',
-        features: [],
-    };
-
     @action
     displayRoute = (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => {
         if (routeInfoMap) {
@@ -1096,41 +784,23 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
             };
             // TODO: Create pin for destination
             // TODO: Fly to point where full route will be shown
-            this.temporaryRouteSource = newTempRouteSource;
+            this._temporaryRouteSource = newTempRouteSource;
         }
     };
 
-    @observable
-    isAnimating: boolean = false;
-
-    @observable
-    routeToAnimate: Doc | undefined = undefined;
-
-    @observable
-    animationPhase: number = 0;
-
-    @observable
-    finishedFlyTo: boolean = false;
-
     @action
     setAnimationPhase = (newValue: number) => {
-        this.animationPhase = newValue;
+        this._animationPhase = newValue;
     };
 
-    @observable
-    frameId: number | null = null;
-
     @action
     setFrameId = (frameId: number) => {
-        this.frameId = frameId;
+        this._frameId = frameId;
     };
 
-    @observable
-    animationUtility: AnimationUtility | null = null;
-
     @action
     setAnimationUtility = (util: AnimationUtility) => {
-        this.animationUtility = util;
+        this._animationUtility = util;
     };
 
     @action
@@ -1138,8 +808,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         if (routeDoc) {
             MapAnchorMenu.Instance.fadeOut(true);
             document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
-            this.featuresFromGeocodeResults = [];
-            this.routeToAnimate = routeDoc;
+            this._featuresFromGeocodeResults = [];
+            this._routeToAnimate = routeDoc;
         }
     };
 
@@ -1161,29 +831,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @computed
     get preAnimationViewState() {
-        if (!this.isAnimating) {
+        if (!this._isAnimating) {
             return this.mapboxMapViewState;
         }
     }
 
-    @observable
-    isStreetViewAnimation: boolean = false;
-
-    @observable
-    animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM;
-
-    @observable
-    animationLineColor: string = '#ffff00';
-
     @action
     setAnimationLineColor = (color: ColorResult) => {
-        this.animationLineColor = color.hex;
+        this._animationLineColor = color.hex;
     };
 
     @action
     updateAnimationSpeed = () => {
         let newAnimationSpeed: AnimationSpeed;
-        switch (this.animationSpeed) {
+        switch (this._animationSpeed) {
             case AnimationSpeed.SLOW:
                 newAnimationSpeed = AnimationSpeed.MEDIUM;
                 break;
@@ -1197,63 +858,33 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 newAnimationSpeed = AnimationSpeed.MEDIUM;
                 break;
         }
-        this.animationSpeed = newAnimationSpeed;
-        if (this.animationUtility) {
-            this.animationUtility.updateAnimationSpeed(newAnimationSpeed);
+        this._animationSpeed = newAnimationSpeed;
+        if (this._animationUtility) {
+            this._animationUtility.updateAnimationSpeed(newAnimationSpeed);
         }
     };
     @computed get animationSpeedTooltipText(): string {
-        switch (this.animationSpeed) {
-            case AnimationSpeed.SLOW:
-                return '1x speed';
-            case AnimationSpeed.MEDIUM:
-                return '2x speed';
-            case AnimationSpeed.FAST:
-                return '3x speed';
-            default:
-                return '2x speed';
-        }
+        switch (this._animationSpeed) {
+            case AnimationSpeed.SLOW:   return '1x speed';
+            case AnimationSpeed.MEDIUM: return '2x speed';
+            case AnimationSpeed.FAST:   return '3x speed';
+            default:                    return '2x speed';
+        } // prettier-ignore
     }
     @computed get animationSpeedIcon(): JSX.Element {
-        switch (this.animationSpeed) {
-            case AnimationSpeed.SLOW:
-                return slowSpeedIcon;
-            case AnimationSpeed.MEDIUM:
-                return mediumSpeedIcon;
-            case AnimationSpeed.FAST:
-                return fastSpeedIcon;
-            default:
-                return mediumSpeedIcon;
-        }
+        switch (this._animationSpeed) {
+            case AnimationSpeed.SLOW:   return slowSpeedIcon;
+            case AnimationSpeed.MEDIUM: return mediumSpeedIcon;
+            case AnimationSpeed.FAST:   return fastSpeedIcon;
+            default:                    return mediumSpeedIcon;
+        } // prettier-ignore
     }
 
     @action
     toggleIsStreetViewAnimation = () => {
-        const newVal = !this.isStreetViewAnimation;
-        this.isStreetViewAnimation = newVal;
-        if (this.animationUtility) {
-            this.animationUtility.updateIsStreetViewAnimation(newVal);
-        }
-    };
-
-    @observable
-    dynamicRouteFeature: Feature<Geometry, GeoJsonProperties> = {
-        type: 'Feature',
-        properties: {},
-        geometry: {
-            type: 'LineString',
-            coordinates: [],
-        },
-    };
-
-    @observable
-    path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = {
-        type: 'Feature',
-        geometry: {
-            type: 'LineString',
-            coordinates: [],
-        },
-        properties: {},
+        const newVal = !this._isStreetViewAnimation;
+        this._isStreetViewAnimation = newVal;
+        this._animationUtility?.updateIsStreetViewAnimation(newVal);
     };
 
     getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => {
@@ -1272,19 +903,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @action
     playAnimation = (status: AnimationStatus) => {
-        if (!this._mapRef.current || !this.routeToAnimate) {
+        if (!this._mapRef.current || !this._routeToAnimate) {
             return;
         }
 
-        this.animationPhase = status === AnimationStatus.RESUME ? this.animationPhase : 0;
-        this.frameId = AnimationStatus.RESUME ? this.frameId : null;
-        this.finishedFlyTo = AnimationStatus.RESUME ? this.finishedFlyTo : false;
+        this._animationPhase = status === AnimationStatus.RESUME ? this._animationPhase : 0;
+        this._frameId = AnimationStatus.RESUME ? this._frameId : null;
+        this._finishedFlyTo = AnimationStatus.RESUME ? this._finishedFlyTo : false;
 
         const path = turf.lineString(this.selectedRouteCoordinates);
 
-        this.settingsOpen = false;
+        this._settingsOpen = false;
         this.path = path;
-        this.isAnimating = true;
+        this._isAnimating = true;
 
         runInAction(() => {
             return new Promise<void>(async resolve => {
@@ -1293,18 +924,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                     lat: this.selectedRouteCoordinates[0][1],
                 };
 
-                const animationUtil = new AnimationUtility(targetLngLat, this.selectedRouteCoordinates, this.isStreetViewAnimation, this.animationSpeed, this.showTerrain, this._mapRef.current);
-                runInAction(() => {
-                    this.setAnimationUtility(animationUtil);
-                });
-
-                const updateFrameId = (newFrameId: number) => {
-                    this.setFrameId(newFrameId);
-                };
+                const animationUtil = new AnimationUtility(targetLngLat, this.selectedRouteCoordinates, this._isStreetViewAnimation, this._animationSpeed, this._showTerrain, this._mapRef.current);
+                runInAction(() => this.setAnimationUtility(animationUtil));
 
-                const updateAnimationPhase = (newAnimationPhase: number) => {
-                    this.setAnimationPhase(newAnimationPhase);
-                };
+                const updateFrameId = (newFrameId: number) => this.setFrameId(newFrameId);
+                const updateAnimationPhase = (newAnimationPhase: number) => this.setAnimationPhase(newAnimationPhase);
 
                 if (status !== AnimationStatus.RESUME) {
                     const result = await animationUtil.flyInAndRotate({
@@ -1324,9 +948,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                     console.log('Altitude: ', result.altitude);
                 }
 
-                runInAction(() => {
-                    this.finishedFlyTo = true;
-                });
+                runInAction(() => (this._finishedFlyTo = true));
 
                 // follow the path while slowly rotating the camera, passing in the camera bearing and altitude from the previous animation
                 await animationUtil.animatePath({
@@ -1335,7 +957,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                     // startBearing: -20,
                     // startAltitude: this.isStreetViewAnimation ? 80 : 12000,
                     // pitch: this.isStreetViewAnimation ? 80: 50,
-                    currentAnimationPhase: this.animationPhase,
+                    currentAnimationPhase: this._animationPhase,
                     updateAnimationPhase,
                     updateFrameId,
                 });
@@ -1353,7 +975,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 });
 
                 setTimeout(() => {
-                    this.isStreetViewAnimation = false;
+                    this._isStreetViewAnimation = false;
                     resolve();
                 }, 10000);
             });
@@ -1362,27 +984,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @action
     pauseAnimation = () => {
-        if (this.frameId && this.animationPhase > 0) {
-            window.cancelAnimationFrame(this.frameId);
-            this.frameId = null;
-            this.isAnimating = false;
+        if (this._frameId && this._animationPhase > 0) {
+            window.cancelAnimationFrame(this._frameId);
+            this._frameId = null;
+            this._isAnimating = false;
         }
     };
 
     @action
     stopAnimation = (close: boolean) => {
-        if (this.frameId) {
-            window.cancelAnimationFrame(this.frameId);
+        if (this._frameId) {
+            window.cancelAnimationFrame(this._frameId);
         }
-        this.animationPhase = 0;
-        this.frameId = null;
-        this.finishedFlyTo = false;
-        this.isAnimating = false;
+        this._animationPhase = 0;
+        this._frameId = null;
+        this._finishedFlyTo = false;
+        this._isAnimating = false;
         if (close) {
-            this.animationSpeed = AnimationSpeed.MEDIUM;
-            this.isStreetViewAnimation = false;
-            this.routeToAnimate = undefined;
-            this.animationUtility = null;
+            this._animationSpeed = AnimationSpeed.MEDIUM;
+            this._isStreetViewAnimation = false;
+            this._routeToAnimate = undefined;
+            this._animationUtility = null;
         }
     };
 
@@ -1390,21 +1012,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         return (
             <>
                 <IconButton
-                    tooltip={this.isAnimating && this.finishedFlyTo ? 'Pause Animation' : 'Play Animation'}
+                    tooltip={this._isAnimating && this._finishedFlyTo ? 'Pause Animation' : 'Play Animation'}
                     onPointerDown={() => {
-                        if (this.isAnimating && this.finishedFlyTo) {
+                        if (this._isAnimating && this._finishedFlyTo) {
                             this.pauseAnimation();
-                        } else if (this.animationPhase > 0) {
+                        } else if (this._animationPhase > 0) {
                             this.playAnimation(AnimationStatus.RESUME); // Resume from the current phase
                         } else {
                             this.playAnimation(AnimationStatus.START); // Play from the beginning
                         }
                     }}
-                    icon={this.isAnimating && this.finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />}
+                    icon={this._isAnimating && this._finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />}
                     color="black"
                     size={Size.MEDIUM}
                 />
-                {this.isAnimating && this.finishedFlyTo && (
+                {this._isAnimating && this._finishedFlyTo && (
                     <IconButton
                         tooltip="Restart animation"
                         onPointerDown={() => {
@@ -1420,7 +1042,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 <>
                     <div className="animation-suboptions">
                         <div>|</div>
-                        <FormControlLabel className="first-person-label" label="1st person animation:" labelPlacement="start" control={<Checkbox color="success" checked={this.isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} />
+                        <FormControlLabel className="first-person-label" label="1st person animation:" labelPlacement="start" control={<Checkbox color="success" checked={this._isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} />
                         <div id="divider">|</div>
                         <IconButton tooltip={this.animationSpeedTooltipText} onPointerDown={this.updateAnimationSpeed} icon={this.animationSpeedIcon} size={Size.MEDIUM} />
                         <div id="divider">|</div>
@@ -1436,26 +1058,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
 
     @action
     hideRoute = () => {
-        this.temporaryRouteSource = {
+        this._temporaryRouteSource = {
             type: 'FeatureCollection',
             features: [],
         };
     };
 
-    @observable
-    settingsOpen: boolean = false;
-
-    @observable
-    mapStyle: string = 'mapbox://styles/mapbox/standard';
-
-    @observable
-    showTerrain: boolean = true;
-
     @action
     toggleSettings = () => {
-        if (!this.isAnimating && this.animationPhase == 0) {
-            this.featuresFromGeocodeResults = [];
-            this.settingsOpen = !this.settingsOpen;
+        if (!this._isAnimating && this._animationPhase == 0) {
+            this._featuresFromGeocodeResults = [];
+            this._settingsOpen = !this._settingsOpen;
         }
     };
 
@@ -1500,14 +1113,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
     @action
     onStepZoomChange = (increment: boolean) => {
         if (this._mapRef.current) {
-            let newZoom: number;
-            if (increment) {
-                console.log('inc');
-                newZoom = Math.min(16, this.mapboxMapViewState.zoom + 1);
-            } else {
-                console.log('dec');
-                newZoom = Math.max(0, this.mapboxMapViewState.zoom - 1);
-            }
+            const newZoom = increment //
+                ? Math.min(16, this.mapboxMapViewState.zoom + 1)
+                : Math.max(0, this.mapboxMapViewState.zoom - 1);
+
             this._mapRef.current.setZoom(newZoom);
             this.dataDoc.map_zoom = newZoom;
         }
@@ -1523,7 +1132,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
     };
 
     @action
-    toggleShowTerrain = () => (this.showTerrain = !this.showTerrain);
+    toggleShowTerrain = () => (this._showTerrain = !this._showTerrain);
 
     getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
         const markerType = StrCast(pinDoc.markerType);
@@ -1532,25 +1141,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null;
     };
 
-    static _firstRender = true;
-    static _rerenderDelay = 500;
-    _rerenderTimeout: any;
+    _textRef = React.createRef<any>();
     render() {
-        // bcz:  no idea what's going on here, but bings maps have some kind of bug
-        // such that we need to delay rendering a second map on startup until the first map is rendered.
-        this.Document[DocCss];
-        if (MapBox._rerenderDelay) {
-            // prettier-ignore
-            this._rerenderTimeout = this._rerenderTimeout ??
-                setTimeout(action(() => {
-                    if ((window as any).Microsoft?.Maps?.Internal._WorkDispatcher) {
-                        MapBox._rerenderDelay = 0;
-                    }
-                    this._rerenderTimeout = undefined;
-                    this.Document[DocCss] = this.Document[DocCss] + 1;
-                }), MapBox._rerenderDelay);
-            return null;
-        }
         const scale = this._props.NativeDimScaling?.() || 1;
         const parscale = scale === 1 ? 1 : this.ScreenToLocalBoxXf().Scale ?? 1;
 
@@ -1560,20 +1152,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                 <div
                     className="mapBox-wrapper"
                     onWheel={e => e.stopPropagation()}
-                    onPointerDown={async e => {
-                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
-                    }}
+                    onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
                     style={{ transformOrigin: 'top left', transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                     <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                     {renderAnnotations(this.opaqueFilter)}
                     {SnappingManager.IsDragging ? null : renderAnnotations()}
-                    {!this.routeToAnimate && (
+                    {!this._routeToAnimate && (
                         <div className="mapBox-searchbar" style={{ width: `${100 / scale}%`, zIndex: 1, position: 'relative', background: 'lightGray' }}>
-                            <TextField fullWidth placeholder="Enter a location" onChange={(e: any) => this.handleSearchChange(e.target.value)} />
+                            <TextField ref={this._textRef} fullWidth placeholder="Enter a location" onKeyDown={this.searchbarKeyDown} onChange={(e: any) => this.handleSearchChange(e.target.value)} />
                             <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={e => this.toggleSettings()} />
                         </div>
                     )}
-                    {this.settingsOpen && !this.routeToAnimate && (
+                    {this._settingsOpen && !this._routeToAnimate && (
                         <div className="mapbox-settings-panel" style={{ right: `${0 + this.sidebarWidth()}px` }}>
                             <div className="mapbox-style-select">
                                 <div>Map Style:</div>
@@ -1605,21 +1195,21 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                             </div>
                             <div className="mapbox-terrain-selection">
                                 <div>Show terrain: </div>
-                                <input type="checkbox" checked={this.showTerrain} onChange={this.toggleShowTerrain} />
+                                <input type="checkbox" checked={this._showTerrain} onChange={this.toggleShowTerrain} />
                             </div>
                         </div>
                     )}
-                    {this.routeToAnimate && (
+                    {this._routeToAnimate && (
                         <div className="animation-panel" style={{ width: this.sidebarWidth() === 0 ? '100%' : `calc(100% - ${this.sidebarWidth()}px)` }}>
-                            <div id="route-to-animate-title">{StrCast(this.routeToAnimate.title)}</div>
+                            <div id="route-to-animate-title">{StrCast(this._routeToAnimate.title)}</div>
                             <div className="route-animation-options">{this.getRouteAnimationOptions()}</div>
                         </div>
                     )}
-                    {this.featuresFromGeocodeResults.length > 0 && (
+                    {this._featuresFromGeocodeResults.length > 0 && (
                         <div className="mapbox-geocoding-search-results">
-                            <React.Fragment>
+                            <>
                                 <h4>Choose a location for your pin: </h4>
-                                {this.featuresFromGeocodeResults
+                                {this._featuresFromGeocodeResults
                                     .filter(feature => feature.place_name)
                                     .map((feature, idx) => (
                                         <div
@@ -1632,14 +1222,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                                             <div className="search-result-place-name">{feature.place_name}</div>
                                         </div>
                                     ))}
-                            </React.Fragment>
+                            </>
                         </div>
                     )}
                     <MapProvider>
                         <MapboxMap
                             ref={this._mapRef}
                             mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
-                            viewState={this.isAnimating || this.routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
+                            viewState={this._isAnimating || this._routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
                             mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
                             style={{
                                 position: 'absolute',
@@ -1649,20 +1239,22 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                                 width: NumCast(this.layoutDoc._width) * parscale,
                                 height: NumCast(this.layoutDoc._height) * parscale,
                             }}
-                            initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState}
+                            initialViewState={this._isAnimating ? undefined : this.mapboxMapViewState}
                             onZoom={this.onMapZoom}
                             onMove={this.onMapMove}
                             onClick={this.handleMapClick}
                             onDblClick={this.handleMapDblClick}
-                            terrain={this.showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined}>
+                            terrain={this._showTerrain ? { source: 'mapbox-dem', exaggeration: 2.0 } : undefined}>
                             <Source id="mapbox-dem" type="raster-dem" url="mapbox://mapbox.mapbox-terrain-dem-v1" tileSize={512} maxzoom={14} />
-                            <Source id="temporary-route" type="geojson" data={this.temporaryRouteSource} />
+                            <Source id="temporary-route" type="geojson" data={this._temporaryRouteSource} />
                             <Source id="map-routes" type="geojson" data={this.allRoutesGeoJson} />
                             <Layer id="temporary-route-layer" type="line" source="temporary-route" layout={{ 'line-join': 'round', 'line-cap': 'round' }} paint={{ 'line-color': '#36454F', 'line-width': 4, 'line-dasharray': [1, 1] }} />
-                            {!this.isAnimating && this.animationPhase == 0 && <Layer id="map-routes-layer" type="line" source="map-routes" layout={{ 'line-join': 'round', 'line-cap': 'round' }} paint={{ 'line-color': '#FF0000', 'line-width': 4 }} />}
-                            {this.routeToAnimate && (this.isAnimating || this.animationPhase > 0) && (
+                            {!this._isAnimating && this._animationPhase == 0 && (
+                                <Layer id="map-routes-layer" type="line" source="map-routes" layout={{ 'line-join': 'round', 'line-cap': 'round' }} paint={{ 'line-color': '#FF0000', 'line-width': 4 }} />
+                            )}
+                            {this._routeToAnimate && (this._isAnimating || this._animationPhase > 0) && (
                                 <>
-                                    {!this.isStreetViewAnimation && (
+                                    {!this._isStreetViewAnimation && (
                                         <>
                                             <Source id="animated-route" type="geojson" data={this.updatedRouteCoordinates} />
                                             <Layer
@@ -1670,7 +1262,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                                                 type="line"
                                                 source="animated-route"
                                                 paint={{
-                                                    'line-color': this.animationLineColor,
+                                                    'line-color': this._animationLineColor,
                                                     'line-width': 5,
                                                 }}
                                             />
@@ -1722,10 +1314,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                             )}
 
                             <>
-                                {!this.isAnimating &&
-                                    this.animationPhase == 0 &&
-                                    this.allPushpins
-                                        // .filter(anno => !anno.layout_unrendered)
+                                {!this._isAnimating &&
+                                    this._animationPhase == 0 &&
+                                    this.allPushpins // .filter(anno => !anno.layout_unrendered)
                                         .map((pushpin, idx) => (
                                             <Marker key={idx} longitude={NumCast(pushpin.longitude)} latitude={NumCast(pushpin.latitude)} anchor="bottom" onClick={(e: MarkerEvent<mapboxgl.Marker, MouseEvent>) => this.handleMarkerClick(e, pushpin)}>
                                                 {this.getMarkerIcon(pushpin)}
-- 
cgit v1.2.3-70-g09d2


From ad1b41beaa0c55487d41381bddd80ab1a76e7fb5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 30 Mar 2024 00:36:19 -0400
Subject: added Labels button for turning ink labels on/off.  added Pixels and
 Rotate buttons for images.  enabled ink as template for text : check for ink
 stroke data on layout doc if it's not on data doc & started to have style
 provider check for properties on layout doc instead of data (background
 color, boxShadow).   fixed toggling key/value on and off when node is
 selected.

---
 src/client/documents/Documents.ts                  |  2 ++
 src/client/util/CurrentUserUtils.ts                | 16 ++++++++---
 src/client/views/InkControlPtHandles.tsx           |  4 +--
 src/client/views/InkingStroke.tsx                  | 12 ++++++--
 src/client/views/StyleProvider.tsx                 | 17 ++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +--
 src/client/views/global/globalScripts.ts           | 21 ++++++++++++--
 src/client/views/nodes/DocumentView.scss           |  1 -
 src/client/views/nodes/MapBox/MapBox.tsx           |  3 ++
 .../views/nodes/formattedText/DashFieldView.tsx    | 32 ++++++++++------------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 .../views/nodes/formattedText/RichTextRules.ts     |  3 +-
 src/client/views/nodes/formattedText/nodes_rts.ts  |  1 -
 13 files changed, 74 insertions(+), 44 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 6275de405..6965ae0ce 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1804,10 +1804,12 @@ export namespace DocUtils {
         const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data);
         const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data);
         const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data);
+        const userTypes = DocListCast(Cast(Doc.UserDoc()['template_user'], Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const allTemplates = iconViews
             .concat(templBtns)
             .concat(noteTypes)
+            .concat(userTypes)
             .concat(clickFuncs)
             .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
             .filter(doc => doc.isTemplateDoc);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 90368c1eb..3855bdeb6 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -764,6 +764,7 @@ pie title Minerals in my tap water
             { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square",     toolType:GestureUtils.Gestures.Rectangle,   scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
             { title: "Line",   toolTip: "Line (double tap to lock mode)",   btnType: ButtonType.ToggleButton, icon: "minus",      toolType:GestureUtils.Gestures.Line,        scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
             { title: "Mask",   toolTip: "Mask",                             btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask",                        scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
+            { title: "Labels", toolTip: "Lab els",                          btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels",                         scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, },
             { title: "Width",  toolTip: "Stroke width",                     btnType: ButtonType.NumberSliderButton,               toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
             { title: "Ink",    toolTip: "Stroke color",                     btnType: ButtonType.ColorButton,  icon: "pen",        toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'} },
         ];
@@ -772,7 +773,7 @@ pie title Minerals in my tap water
     static schemaTools():Button[] {
         return [
             {title: "Preview", toolTip: "Show selection preview", btnType: ButtonType.ToggleButton, icon: "portrait", scripts:{ onClick: '{ return toggleSchemaPreview(_readOnly_); }'} },
-            {title: "1 Line",  toolTip: "Single Line Rows",       btnType: ButtonType.ToggleButton, icon: "eye", scripts:{ onClick: '{ return toggleSingleLineSchema(_readOnly_); }'} },
+            {title: "1 Line",  toolTip: "Single Line Rows",       btnType: ButtonType.ToggleButton, icon: "eye",      scripts:{ onClick: '{ return toggleSingleLineSchema(_readOnly_); }'} },
             {title: "DataViz", toolTip: "Turn Schema Table into Data Visualization Doc",       btnType: ButtonType.ClickButton, icon: "chart-bar", scripts:{ onClick: '{ datavizFromSchema()'} }, ];
     }
 
@@ -780,12 +781,18 @@ pie title Minerals in my tap water
         return [
             { title: "Back",    toolTip: "Go back",         btnType: ButtonType.ClickButton,  icon: "arrow-left",              scripts: { onClick: '{ return webBack(); }' }},
             { title: "Forward", toolTip: "Go forward",      btnType: ButtonType.ClickButton,  icon: "arrow-right",             scripts: { onClick: '{ return webForward(); }'}},
-            { title: "URL",     toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, scripts: { script: '{ return webSetURL(value, _readOnly_); }'} },
+            { title: "URL",     toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, scripts: { script:  '{ return webSetURL(value, _readOnly_); }'} },
         ];
     }
     static videoTools() {
         return [
-            { title: "Snapshot",toolTip: "Take snapshot of current frame", btnType: ButtonType.ClickButton,  icon: "camera",                  scripts: { onClick: '{ return videoSnapshot(); }' }},
+            { title: "Snapshot",toolTip: "Take snapshot of current frame", btnType: ButtonType.ClickButton,  icon: "camera",   scripts: { onClick: '{ return videoSnapshot(); }' }},
+        ];
+    }
+    static imageTools() {
+        return [
+            { title: "Pixels",toolTip: "Set Native Pixel Sizze", btnType: ButtonType.ClickButton,  icon: "portrait",  scripts: { onClick: 'imageSetPixelSize();' }},
+            { title: "Rotate",toolTip: "Rotate 90",              btnType: ButtonType.ClickButton,  icon: "redo-alt",  scripts: { onClick: 'imageRotate90();' }},
         ];
     }
     static contextMenuTools():Button[] {
@@ -810,7 +817,8 @@ pie title Minerals in my tap water
             { title: "View",    icon: "View",          toolTip: "View tools",           subMenu: CurrentUserUtils.viewTools(),   expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
             { title: "Stack",   icon: "View",          toolTip: "Stacking tools",       subMenu: CurrentUserUtils.stackTools(),  expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
             { title: "Web",     icon: "Web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    expertMode: false, toolType:DocumentType.WEB,            funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
-            { title: "Video",   icon: "Video",         toolTip: "Video functions",      subMenu: CurrentUserUtils.videoTools(),  expertMode: false, toolType:DocumentType.VID,            funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
+            { title: "Video",   icon: "Video",         toolTip: "Video functions",      subMenu: CurrentUserUtils.videoTools(),  expertMode: false, toolType:DocumentType.VID,            funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
+            { title: "Image",   icon: "Image",         toolTip: "Image functions",      subMenu: CurrentUserUtils.imageTools(),  expertMode: false, toolType:DocumentType.IMG,            funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
             { title: "Schema",  icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions",subMenu: CurrentUserUtils.schemaTools(),expertMode: false,toolType:CollectionViewType.Schema,funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Schema is selected
            ];
     }
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 01d52135a..edc6b404b 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -239,8 +239,8 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> {
         const hdl = (key: string, pt: PointData, dragFunc: (e: React.PointerEvent) => void) => (
             <circle
                 key={key}
-                cx={pt.X}
-                cy={pt.Y}
+                cx={pt?.X}
+                cy={pt?.Y}
                 r={this._props.screenSpaceLineWidth * 2}
                 fill={this._overStart ? '#aaaaaa' : '#99999977'}
                 stroke={'#00007777'}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 345309b4a..5a9f10ba4 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -217,7 +217,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
      * factor for converting between ink and screen space.
      */
     inkScaledData = () => {
-        const inkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
+        const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null))?.inkData ?? [];
         const inkStrokeWidth = NumCast(this.layoutDoc.stroke_width, 1);
         const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2;
         const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2;
@@ -446,7 +446,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
                     {clickableLine(this.onPointerDown, isInkMask)}
                     {isInkMask ? null : inkLine}
                 </svg>
-                {!closed || (!RTFCast(this.dataDoc.text)?.Text && !this.dataDoc[this.fieldKey + '_showLabel'] && (!this._props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
+                {!closed || this.dataDoc[this.fieldKey + '_showLabel'] === false || (!RTFCast(this.dataDoc.text)?.Text && !this.dataDoc[this.fieldKey + '_showLabel'] && (!this._props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
                     <div
                         className="inkStroke-text"
                         style={{
@@ -464,7 +464,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
                             yPadding={10}
                             xPadding={10}
                             fieldKey="text"
-                            dontRegisterView={true}
+                            //dontRegisterView={true}
                             noSidebar={true}
                             dontScale={true}
                             isContentActive={this._props.isContentActive}
@@ -488,6 +488,9 @@ export function SetActiveInkColor(value: string) {
 export function SetActiveIsInkMask(value: boolean) {
     ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value);
 }
+export function SetActiveInkHideTextLabels(value: boolean) {
+    ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value);
+}
 export function SetActiveFillColor(value: string) {
     ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
 }
@@ -515,6 +518,9 @@ export function ActiveFillColor(): string {
 export function ActiveIsInkMask(): boolean {
     return BoolCast(ActiveInkPen()?.activeIsInkMask, false);
 }
+export function ActiveInkHideTextLabels(): boolean {
+    return BoolCast(ActiveInkPen().activeInkHideTextLabels, false);
+}
 export function ActiveArrowStart(): string {
     return StrCast(ActiveInkPen()?.activeArrowStart, '');
 }
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index bf6f2a4fa..8073d0842 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -89,7 +89,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
     const isNonTransparent = property.includes(':nonTransparent');
     const isNonTransparentLevel = isNonTransparent ? Number(property.replace(/.*:nonTransparent([0-9]+).*/, '$1')) : 0; // property.includes(':nonTransparent');
     const isAnnotated = property.includes(':annotated');
-    const isInk = () => doc?._layout_isSvg && !props?.LayoutTemplateString;
+    const layoutDoc = doc ? Doc.Layout(doc) : doc;
+    const isInk = () => layoutDoc?._layout_isSvg && !props?.LayoutTemplateString;
     const isOpen = property.includes(':open');
     const isEmpty = property.includes(':empty');
     const boxBackground = property.includes(':box');
@@ -129,7 +130,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                         highlightStyle: doc.isGroup ? "dotted": highlightStyle,
                         highlightColor,
                         highlightIndex,
-                        highlightStroke: doc.layout_isSvg,
+                        highlightStroke: layoutDoc?.layout_isSvg,
                     };
                 }
             }
@@ -203,7 +204,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
             let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?.backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
             if (doc?.[StrCast(doc?.layout_fieldKey)] instanceof Doc) docColor = StrCast(doc._backgroundColor,docColor)
             // prettier-ignore
-            switch (doc?.type) {
+            switch (layoutDoc?.type) {
                 case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
                 case DocumentType.PRES:        docColor = docColor || 'transparent'; break;
                 case DocumentType.FONTICON:    docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
@@ -220,13 +221,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                 case DocumentType.SCREENSHOT:
                 case DocumentType.VID:         docColor = docColor || (Colors.LIGHT_GRAY); break;
                 case DocumentType.COL:
-                    docColor = docColor || (Doc.IsSystem(doc)
+                    docColor = docColor || (doc && Doc.IsSystem(doc)
                             ? SettingsManager.userBackgroundColor
-                            : doc.annotationOn
+                            : doc?.annotationOn
                             ? '#00000010' // faint interior for collections on PDFs, images, etc
                             : doc?.isGroup
                             ? undefined
-                            : doc._type_collection === CollectionViewType.Stacking ?
+                            : doc?._type_collection === CollectionViewType.Stacking ?
                             (Colors.DARK_GRAY)
                             : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY));
                     break;
@@ -241,7 +242,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
         case StyleProp.BoxShadow: {
             if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow)
             if (doc.layout_boxShadow === 'standard') return Shadows.STANDARD_SHADOW;
-            if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && !doc.layout_isSvg) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
+            if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && !layoutDoc?.layout_isSvg) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em');
             switch (doc?.type) {
                 case DocumentType.COL:
                     return StrCast(
@@ -266,7 +267,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
                         ? undefined // if it's a background & has a cluster color, make the shadow spread really big
                         : fieldKey.includes('_inline')  // if doc is an inline document in a text box
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
-                        : DocCast(doc.embedContainer)?.type=== DocumentType.RTF // if doc is embedded in a text document (but not an inline)
+                        : DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline)
                         ? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
                         : StrCast(doc.layout_boxShadow, '');
             }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5495735ea..c53bad509 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -649,7 +649,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                       y: B.y - inkWidth / 2,
                       _width: B.width + inkWidth,
                       _height: B.height + inkWidth,
-                      stroke_showLabel: true}, // prettier-ignore
+                      stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
                     inkWidth
                 );
                 if (Doc.ActiveTool === InkTool.Write) {
@@ -1721,7 +1721,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
                 vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]); // right line
             });
-        SnappingManager.addSnapLines(horizLines, vertLines);
+        this.layoutDoc._freeform_snapLines && SnappingManager.addSnapLines(horizLines, vertLines);
     };
 
     incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index d785c155b..3ea7bfbcd 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -12,7 +12,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { UndoManager, undoable } from '../../util/UndoManager';
 import { GestureOverlay } from '../GestureOverlay';
-import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke';
+import { ActiveFillColor, ActiveInkColor, ActiveInkHideTextLabels, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkHideTextLabels, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 // import { InkTranscription } from '../InkTranscription';
 import { DocData } from '../../../fields/DocSymbols';
@@ -21,6 +21,7 @@ import { DocumentView } from '../nodes/DocumentView';
 import { VideoBox } from '../nodes/VideoBox';
 import { WebBox } from '../nodes/WebBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { ImageBox } from '../nodes/ImageBox';
 
 ScriptingGlobals.add(function IsNoneSelected() {
     return SelectionManager.Views.length <= 0;
@@ -336,15 +337,20 @@ function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean,
 ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode');
 
 // toggle: Set overlay status of selected document
-ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) {
+ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) {
     const selected = SelectionManager.Docs.lastElement() ?? Doc.UserDoc();
     // prettier-ignore
-    const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
+    const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
         ['inkMask', {
             checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())),
             setInk: (doc: Doc) => (doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask),
             setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
         }],
+        ['labels', {
+            checkResult: () => ((selected?._stroke_showLabel ? BoolCast(selected[DocData].stroke_showLabel) : ActiveInkHideTextLabels())),
+            setInk: (doc: Doc) => (doc[DocData].stroke_showLabel = !doc.stroke_showLabel),
+            setMode: () => selected?.type !== DocumentType.INK && SetActiveInkHideTextLabels(!ActiveInkHideTextLabels()),
+        }],
         ['fillColor', {
             checkResult: () => (selected?._layout_isSvg ? StrCast(selected[DocData].fillColor) : ActiveFillColor() ?? "transparent"),
             setInk: (doc: Doc) => (doc[DocData].fillColor = StrCast(value)),
@@ -399,6 +405,15 @@ ScriptingGlobals.add(function videoSnapshot() {
     selected?.Snapshot();
 });
 
+ScriptingGlobals.add(function imageSetPixelSize() {
+    const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox;
+    selected?.setNativeSize();
+});
+ScriptingGlobals.add(function imageRotate90() {
+    const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox;
+    selected?.rotate();
+});
+
 /** Schema
  *      toggleSchemaPreview
  **/
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 5421c1b50..4fea6b3c6 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -256,6 +256,5 @@
 
     .documentView-node:first-child {
         position: relative;
-        background: '#B59B66'; //$white;
     }
 }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 36dad2747..b73898f59 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1161,6 +1161,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                         <div className="mapBox-searchbar" style={{ width: `${100 / scale}%`, zIndex: 1, position: 'relative', background: 'lightGray' }}>
                             <TextField ref={this._textRef} fullWidth placeholder="Enter a location" onKeyDown={this.searchbarKeyDown} onChange={(e: any) => this.handleSearchChange(e.target.value)} />
                             <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={e => this.toggleSettings()} />
+                            <div style={{ opacity: 0 }}>
+                                <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={e => this.toggleSettings()} />
+                            </div>
                         </div>
                     )}
                     {this._settingsOpen && !this._routeToAnimate && (
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index cdfeebe66..f6ca97ee9 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -29,8 +29,8 @@ export class DashFieldView {
     root: any;
     node: any;
     tbox: FormattedTextBox;
-    @observable expanded = false;
-    Expanded = () => this.expanded;
+    @observable _nodeSelected = false;
+    NodeSelected = () => this._nodeSelected;
 
     unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
@@ -71,8 +71,6 @@ export class DashFieldView {
             e.stopPropagation();
         };
 
-        this.expanded = node.attrs.expanded;
-
         this.root = ReactDOM.createRoot(this.dom);
         this.root.render(
             <DashFieldViewInternal
@@ -86,7 +84,7 @@ export class DashFieldView {
                 hideKey={node.attrs.hideKey}
                 hideValue={node.attrs.hideValue}
                 editable={node.attrs.editable}
-                expanded={this.Expanded}
+                nodeSelected={this.NodeSelected}
                 dataDoc={node.attrs.dataDoc}
                 tbox={tbox}
             />
@@ -100,11 +98,11 @@ export class DashFieldView {
         });
     }
     deselectNode() {
-        runInAction(() => (this.expanded = false));
+        runInAction(() => (this._nodeSelected = false));
         this.dom.classList.remove('ProseMirror-selectednode');
     }
     selectNode() {
-        setTimeout(() => runInAction(() => (this.expanded = true)), 100);
+        setTimeout(() => runInAction(() => (this._nodeSelected = true)), 100);
         this.dom.classList.add('ProseMirror-selectednode');
     }
 }
@@ -118,7 +116,7 @@ interface IDashFieldViewInternal {
     width: number;
     height: number;
     editable: boolean;
-    expanded: () => boolean;
+    nodeSelected: () => boolean;
     dataDoc: boolean;
     node: any;
     getPos: any;
@@ -132,7 +130,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     _fieldKey: string;
     _fieldRef = React.createRef<HTMLDivElement>();
     @observable _dashDoc: Doc | undefined = undefined;
-    @observable _expanded = this._props.expanded();
+    @observable _expanded = this._props.nodeSelected();
 
     constructor(props: IDashFieldViewInternal) {
         super(props);
@@ -158,7 +156,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     componentWillUnmount() {
         this._reactionDisposer?.();
     }
-    isRowActive = () => (this._props.expanded() || this._expanded) && this._props.editable;
+    isRowActive = () => (this._props.nodeSelected() || this._expanded) && this._props.editable;
 
     finishEdit = action(() => {
         if (this._expanded) {
@@ -181,7 +179,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
                     deselectCell={emptyFunction}
                     selectCell={emptyFunction}
                     maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth}
-                    columnWidth={this._expanded || this._props.expanded() ? this.columnWidth : returnZero}
+                    columnWidth={this._expanded || this._props.nodeSelected() ? this.columnWidth : returnZero}
                     selectedCell={this.selectedCell}
                     fieldKey={this._fieldKey}
                     rowHeight={returnZero}
@@ -190,7 +188,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
                     getFinfo={emptyFunction}
                     setColumnValues={returnFalse}
                     allowCRs={true}
-                    oneLine={!this._expanded && !this._props.expanded()}
+                    oneLine={!this._expanded && !this._props.nodeSelected()}
                     finishEdit={this.finishEdit}
                     transform={Transform.Identity}
                     menuTarget={null}
@@ -221,7 +219,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     toggleFieldHide = undoable(
         action(() => {
             const editor = this._props.tbox.EditorView!;
-            editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideKey: !this._props.node.attrs.hideKey ? true : false }));
+            editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideKey: this._props.node.attrs.hideValue ? false : !this._props.node.attrs.hideKey ? true : false }));
         }),
         'hideKey'
     );
@@ -229,18 +227,17 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     toggleValueHide = undoable(
         action(() => {
             const editor = this._props.tbox.EditorView!;
-            editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideValue: !this._props.node.attrs.hideValue ? true : false }));
+            editor.dispatch(editor.state.tr.setNodeMarkup(this._props.getPos(), this._props.node.type, { ...this._props.node.attrs, hideValue: this._props.node.attrs.hideKey ? false : !this._props.node.attrs.hideValue ? true : false }));
         }),
         'hideValue'
     );
 
-    @observable _showValue = false;
     @computed get _hideKey() {
         return this._props.hideKey && !this._expanded;
     }
 
     @computed get _hideValue() {
-        return !this._showValue && this._props.hideValue && !this._expanded;
+        return this._props.hideValue && !this._props.nodeSelected();
     }
 
     // clicking on the label creates a pivot view collection of all documents
@@ -251,7 +248,6 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
             DashFieldViewMenu.toggleFieldHide = this.toggleFieldHide;
             DashFieldViewMenu.toggleValueHide = this.toggleValueHide;
             DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16, this._fieldKey);
-            this._dashDoc?.[this._fieldKey + '_hideValue'] && runInAction(() => (this._showValue = !this._showValue));
             const editor = this._props.tbox.EditorView!;
             setTimeout(() => editor.dispatch(editor.state.tr.setSelection(new NodeSelection(editor.state.doc.resolve(this._props.getPos())))), 100);
         });
@@ -263,7 +259,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
     };
 
     @computed get values() {
-        if (this._props.expanded()) return [];
+        if (this._props.nodeSelected()) return [];
         const vals = FilterPanel.gatherFieldValues(DocListCast(Doc.ActiveDashboard?.data), this._fieldKey, []);
 
         return vals.strings.map(facet => ({ value: facet, label: facet }));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a0de822ac..3b583771a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1366,7 +1366,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
             DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
                 if (pdfAnchor instanceof Doc) {
                     const dashField = view.state.schema.nodes.paragraph.create({}, [
-                        view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, hideValue: false, editable: false, expanded: true }, undefined, [
+                        view.state.schema.nodes.dashField.create({ fieldKey: 'text', docId: pdfAnchor[Id], hideKey: true, hideValue: false, editable: false }, undefined, [
                             view.state.schema.marks.linkAnchor.create({
                                 allAnchors: [{ href: `/doc/${this.Document[Id]}`, title: this.Document.title, anchorId: `${this.Document[Id]}` }],
                                 title: StrCast(pdfAnchor.title),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 682d4f96f..483035e86 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -250,10 +250,11 @@ export class RichTextRules {
 
                 if (
                     DocListCast((Doc.UserDoc().template_notes as Doc).data)
+                        .concat(DocListCast((Doc.UserDoc().template_user as Doc).data))
                         .map(d => StrCast(d.title))
                         .includes(color)
                 ) {
-                    setTimeout(() => this.TextBox.DocumentView?.().switchViews(true, color));
+                    setTimeout(() => this.TextBox.DocumentView?.().switchViews(true, color, undefined, true));
                     return state.tr.deleteRange(start, end);
                 }
                 if (marks) {
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 6862eecef..e335044ea 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -266,7 +266,6 @@ export const nodes: { [index: string]: NodeSpec } = {
             hideKey: { default: false },
             hideValue: { default: false },
             editable: { default: true },
-            expanded: { default: null },
             dataDoc: { default: false },
         },
         leafText: node => Field.toString((DocServer.GetCachedRefField(node.attrs.docId as string) as Doc)?.[node.attrs.fieldKey as string] as Field),
-- 
cgit v1.2.3-70-g09d2

