diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 8 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 552 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox2.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 16 |
4 files changed, 170 insertions, 410 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index af8cc07ed..d5d6fb2ba 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -158,6 +158,7 @@ export class DocumentOptions { _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); latitude?: NUMt = new NumInfo('latitude coordinate for map views'); longitude?: NUMt = new NumInfo('longitude coordinate for map views'); + map?: STRt = new StrInfo('text location of map'); _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)'); _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden'); _width?: NUMt = new NumInfo('displayed width of a document'); @@ -295,8 +296,9 @@ export class DocumentOptions { config_latitude?: NUMt = new NumInfo('latitude of a map'); // latitude of a map config_longitude?: NUMt = new NumInfo('longitude of map'); // longitude of map - config_mapZoom?: NUMt = new NumInfo('zoom of map'); // zoom of map - config_mapType?: string; + config_map_zoom?: NUMt = new NumInfo('zoom of map'); // zoom of map + config_map_type?: string; + config_map?: string; config_panX?: NUMt = new NumInfo('panX saved as a view spec'); config_panY?: NUMt = new NumInfo('panY saved as a view spec'); config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec'); @@ -527,7 +529,7 @@ export namespace Docs { DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' }, + options: { map: '', _height: 600, _width: 800, nativeDimModifiable: true, systemIcon: 'BsFillPinMapFill' }, }, ], [ diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 62d622fd6..94944b83d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,19 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { GoogleMapProps, Marker } from '@react-google-maps/api'; import BingMapsReact from 'bingmaps-react'; import { Button, EditableText, IconButton, Type } from 'browndash-components'; -import { docs_v1 } from 'googleapis'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { TbHeartMinus } from 'react-icons/tb'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { Highlight, Width } from '../../../../fields/DocSymbols'; -import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; -import { ScriptField } from '../../../../fields/ScriptField'; import { DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -24,9 +19,8 @@ import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; -import { Annotation } from '../../pdf/Annotation'; import { SidebarAnnos } from '../../SidebarAnnos'; -import { DocumentView, OpenWhere } from '../DocumentView'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PinProps, PresBox } from '../trails'; import { MapAnchorMenu } from './MapAnchorMenu'; @@ -45,29 +39,8 @@ import './MapBox.scss'; * 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 _global = (window /* browser */ || global /* node */) as any; - -const mapContainerStyle = { - height: '100%', -}; - -const defaultCenter = { - lat: 42.360081, - lng: -71.058884, -}; - -const mapOptions = { - fullscreenControl: false, -}; - const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey> -// const script = document.createElement('script'); -// script.defer = true; -// script.async = true; -// script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`; -// document.head.appendChild(script); - /** * Consider integrating later: allows for drawing, circling, making shapes on map */ @@ -85,169 +58,49 @@ const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing // }); @observer -export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() { - private _dropDisposer?: DragManager.DragDropDisposer; - private _disposers: { [name: string]: IReactionDisposer } = {}; - private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); - @observable private _overlayAnnoInfo: Opt<Doc>; - showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno)); +export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); } - public get SidebarKey() { - return this.fieldKey + '_sidebar'; - } + private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); + private _sidebarRef = React.createRef<SidebarAnnos>(); + private _ref: React.RefObject<HTMLDivElement> = React.createRef(); + private _disposers: { [key: string]: IReactionDisposer } = {}; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); - @computed get inlineTextAnnotations() { - return this.allMapMarkers.filter(a => a.text_inlineAnnotations); - } - @observable private _map: google.maps.Map = null as unknown as google.maps.Map; - @observable private selectedPlace: Doc | undefined; - @observable private markerMap: { [id: string]: google.maps.Marker } = {}; - @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; @observable private _marqueeing: number[] | undefined; - @observable private inputRef = React.createRef<HTMLInputElement>(); - @observable private searchMarkers: google.maps.Marker[] = []; - @observable private searchBox = undefined as any; // new window.google.maps.places.Autocomplete(this.inputRef.current!, options); @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); + @computed get inlineTextAnnotations() { + return this.allMapMarkers.filter(a => a.text_inlineAnnotations); + } @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); } - @observable private toggleAddMarker = false; - private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); - @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'; + } - static _canAnnotate = true; - static _hadSelection: boolean = false; - private _sidebarRef = React.createRef<SidebarAnnos>(); - private _ref: React.RefObject<HTMLDivElement> = React.createRef(); - private _disposer: { [key: string]: IReactionDisposer } = {}; componentDidMount() { this.props.setContentView?.(this); } componentWillUnmount(): void { this.deselectPin(); - Object.keys(this._disposer).forEach(key => this._disposer[key]?.()); + Object.keys(this._disposers).forEach(key => this._disposers[key]?.()); } - // iterate allMarkers to size, center, and zoom map to contain all markers - private fitBounds = (map: google.maps.Map) => { - const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds(); - const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean); - !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds())); - }; - - /** - * Load and render all map markers - * @param marker - * @param place - */ - @action - private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => { - place[Id] ? (this.markerMap[place[Id]] = marker) : null; - }; - - /** - * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true - * @param e - * @param place - */ - @action - private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => { - // set which place was clicked - this.selectedPlace = place; - // place.infoWindowOpen = true; - }; - - /** - * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list - * @param position - the LatLng position where the marker is placed - * @param map - */ - @action - private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { - const marker = new google.maps.Marker({ - position: position, - map: map, - }); - map.panTo(position); - const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); - this.addDocument(mapMarker, this.annotationKey); - }; - - _loadPending = true; - - @action - centered = () => { - if (this._loadPending && this._map.getBounds()) { - this._loadPending = false; - this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); - } - this.dataDoc.mapLat = this._map.getCenter()?.lat(); - this.dataDoc.mapLng = this._map.getCenter()?.lng(); - }; - - @action - zoomChanged = () => { - if (this._loadPending && this._map.getBounds()) { - this._loadPending = false; - this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); - } - this.dataDoc.mapZoom = this._map.getZoom(); - }; - - /** - * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted; - * add a customized temporary marker on the map - */ - @action - private handlePlaceChanged = () => { - const place = this.searchBox.getPlace(); - - if (!place.geometry || !place.geometry.location) { - // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed - window.alert("No details available for input: '" + place.name + "'"); - return; - } - - // zoom in on the location of the search result - if (place.geometry.viewport) { - this._map.fitBounds(place.geometry.viewport); - } else { - this._map.setCenter(place.geometry.location); - this._map.setZoom(17); - } - - // customize icon => customized icon for the nature of the location selected - const icon = { - url: place.icon as string, - size: new google.maps.Size(71, 71), - origin: new google.maps.Point(0, 0), - anchor: new google.maps.Point(17, 34), - scaledSize: new google.maps.Size(25, 25), - }; - - // put temporary cutomized marker on searched location - this.searchMarkers.forEach(marker => { - marker.setMap(null); - }); - this.searchMarkers = []; - this.searchMarkers.push( - new window.google.maps.Marker({ - map: this._map, - icon, - title: place.name, - position: place.geometry.location, - }) - ); - }; /** * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts @@ -256,7 +109,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps * @returns */ sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { - console.log('print all sidebar Docs'); if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); const docs = doc instanceof Doc ? [doc] : doc; docs.forEach(doc => { @@ -280,11 +132,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps * @param sidebarKey * @returns */ - sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => { - // if (this.layoutDoc._layout_showSidebar) this.toggleSidebar(); - const docs = doc instanceof Doc ? [doc] : doc; - return this.removeDocument(doc, sidebarKey); - }; + sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey); /** * Toggle sidebar onclick the tiny comment button on the top right corner @@ -317,14 +165,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') ); }; - - sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); - @computed get layout_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')); - } + sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); /** * Handles toggle of sidebar on click the little comment button @@ -349,27 +190,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // TODO: Adding highlight box layer to Maps @action toggleSidebar = () => { - //1.2 * w * ? = .2 * w .2/1.2 const prevWidth = this.sidebarWidth(); this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; createNoteAnnotation = () => { - !this.layoutDoc.layout_showSidebar && this.toggleSidebar(); - - setTimeout( - undoable( - action(() => { - const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); - if (note && this.selectedPin) { - note.latitude = this.selectedPin.latitude; - note.longitude = this.selectedPin.latitude; - } - }), - 'create note annotation' - ) + const createFunc = undoable( + action(() => { + const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false)); + if (note && this.selectedPin) { + note.latitude = this.selectedPin.latitude; + note.longitude = this.selectedPin.longitude; + note.map = this.selectedPin.map; + } + }), + 'create note annotation' ); + if (!this.layoutDoc.layout_showSidebar) { + this.toggleSidebar(); + setTimeout(createFunc); + } else createFunc(); }; sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); @@ -406,37 +247,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); }; - addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { - return this.addDocument(doc, annotationKey); - }; + addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey); pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none'); - @computed get annotationLayer() { - return ( - <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}> - {this.inlineTextAnnotations - .sort((a, b) => NumCast(a.y) - NumCast(b.y)) - .map(anno => ( - <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} /> - ))} - </div> - ); - } - - // Old get anchor function - // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; - - /** - * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker - * @returns - */ - private renderMarkers = () => { - return this.allMapMarkers.map(place => ( - <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} /> - )); - }; - panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); @@ -473,25 +287,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } else { this._bingSearchManager.geocode({ where: query, - callback: action((r: any) => { - res(r.results[0].location); - }), + callback: action((r: any) => res(r.results[0].location)), errorCallback: (e: any) => reject(), }); } }); }; - /** - * - * - * ERIC'S BING MAP CODE BELOW - * - * - * - **/ @observable - bingSearchBarContents: any = 'enter city/zip/...'; // For Bing Maps: The contents of the Bing search bar (string) + bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string) geoDataRequestOptions = { entityType: 'PopulatedPlace', @@ -502,14 +306,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps * Creates Pushpin doc and adds it to the list of annotations */ @action - createPushpin = undoable((latitude: number, longitude: number) => { + createPushpin = undoable((latitude: number, longitude: number, map?: string) => { // Stores the pushpin as a MapMarkerDocument const mapMarker = Docs.Create.PushpinDocument( NumCast(latitude), NumCast(longitude), false, [], - {} + { map: map } // ,'pushpinIDamongus'+ this.incrementer++ ); this.addDocument(mapMarker, this.annotationKey); @@ -517,18 +321,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // mapMarker.infoWindowOpen = true; }, 'createpin'); - /* - * Pushpin dblclick - */ - // @action - // pushpinDblClicked = (pin: any, pinDoc?: Doc) => { - // if (pinDoc) this.removePushpin(pinDoc); - // else this._bingMap.current.entities.remove(pin); - // }; - // The pin that is selected - @observable - selectedPin: Doc | undefined; + @observable selectedPin: Doc | undefined; @action deselectPin = () => { @@ -544,6 +338,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this._bingMap.current.entities.push(newpin); this.map_docToPinMap.set(temp, newpin); this.selectedPin = undefined; + this.bingSearchBarContents = this.rootDoc.map; } }; @@ -558,9 +353,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps pushpinClicked = (pinDoc: Doc) => { this.deselectPin(); this.selectedPin = pinDoc; + this.bingSearchBarContents = pinDoc.map; Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match'); - Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.latitude, 'match'); + Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match'); this.recolorPin(this.selectedPin, 'green'); @@ -575,7 +371,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true); document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true); - // Filter sidebar: }; /** @@ -589,54 +384,47 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps * Map OnClick */ @action - mapOnClick = (e: { location: { latitude: any; longitude: any } }) => this.deselectPin(); + mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { + this.props.select(false); + this.deselectPin(); + }; /* * Updates values of layout doc to match the current map */ @action - updateLayout = () => { - this.dataDoc.latitude = this._bingMap.current.getCenter().latitude; - this.dataDoc.longitude = this._bingMap.current.getCenter().longitude; - this.dataDoc.mapZoom = this._bingMap.current.getZoom(); - // if(this.dataDoc.mapType == 'x'){ - // this.dataDoc.locationToLookAt - // } - // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId(); + 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.mapType = this._bingMap.current.getMapTypeId(); - }; + updateMapType = () => (this.dataDoc.map_type = this._bingMap.current.getMapTypeId()); - searched_pin: any; /* * For Bing Maps * Called by search button's onClick * Finds the geocode of the searched contents and sets location to that location **/ @action - bingSearch = async () => { - const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents); - this.dataDoc.latitude = location.latitude; - this.dataDoc.longitude = location.longitude; - this.dataDoc.mapZoom = this._bingMap.current.getZoom(); - // Creates a temporary pin but does not add it to the dataDoc - this.createPushpin(this.dataDoc.latitude, this.dataDoc.longitude); + 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; + }); }; - /** - * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED - */ - // @action - // addAllPins = () => { - // this._bingMap.current.entities.clear(); - // if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin); - // // this.allMapPushpins.map(pin => this.addPushpin(pin)); - // }; - /* * Returns doc w/ relevant info */ @@ -644,12 +432,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.MapanchorDocument({ title: 'MapAnchor:' + this.rootDoc.title, + text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location', config_latitude: NumCast(this.selectedPin?.latitude ?? this.dataDoc.latitude), config_longitude: NumCast(this.selectedPin?.longitude ?? this.dataDoc.longitude), - config_mapZoom: NumCast(this.dataDoc.mapZoom), - config_mapType: StrCast(this.dataDoc.mapType), - // preslocationToLookAt:this.dataDoc.locationToLookAt, - // presType: + config_map_zoom: NumCast(this.dataDoc.map_zoom), + config_map_type: StrCast(this.dataDoc.map_type), + config_map: StrCast(this.selectedPin?.map) || StrCast(this.dataDoc.map), layout_unrendered: true, }); if (anchor) { @@ -684,23 +472,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.map_docToPinMap.set(pin, pushPin); }; - @observable - pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears - /* * Input: pin doc * Removes pin from annotations */ @action - removePushpin = (pinDoc: Doc) => { - // this.allMapPushpins - // this.allMapPushpins.map(pin => this.addPushpin(pin)); - // this._bingMap.current.entities.clear(); + removePushpin = (pinDoc: Doc) => this.removeDocument(pinDoc, this.annotationKey); - this.removeDocument(pinDoc, this.annotationKey); - - // this.dataDoc[this.annotationKey] - }; /* * Removes pushpin from map render */ @@ -725,11 +503,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps tryHideMapAnchorMenu = (e: PointerEvent) => { let target = document.elementFromPoint(e.x, e.y); - - while (target != null) { - if (target === MapAnchorMenu.top.current) { - return; - } + while (target) { + if (target === MapAnchorMenu.top.current) return; target = target.parentElement; } e.stopPropagation(); @@ -738,13 +513,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); }; - // tryhidemenu = e => if( e.parent... == mapanchormenu.top.currrent) do nothing; else hide menu - @action centerOnSelectedPin = () => { if (this.selectedPin) { this.dataDoc.latitude = this.selectedPin.latitude; this.dataDoc.longitude = this.selectedPin.longitude; + this.dataDoc.map = this.selectedPin.map ?? ''; + this.bingSearchBarContents = this.selectedPin.map; } MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu); @@ -777,33 +552,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }, }; - /** - * Toggles mode for placing a pin down on map - */ - @observable - placePinOn = false; - @action - togglePlacePin = () => { - if (this.placePinOn) this.placePinOn = false; - else this.placePinOn = true; - }; - @action - searchbarOnEdit = (newText: string) => { - this.bingSearchBarContents = newText; - }; + searchbarOnEdit = (newText: string) => (this.bingSearchBarContents = newText); recolorPin = (pin: Doc, color?: string) => { this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin)); this.map_docToPinMap.delete(pin); - const newpin = new this.MicrosoftMaps.Pushpin( - new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), - color - ? { - color, - } - : {} - ); + const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), color ? { color } : {}); this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin)); this._bingMap.current.entities.push(newpin); this.map_docToPinMap.set(pin, newpin); @@ -822,29 +577,36 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps alert('NO Map!?'); } this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick); - this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change')); + 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.updateLayout(); - this._disposer.highlight = reaction( + + this._disposers.mapLocation = reaction( + () => this.rootDoc.map, + mapLoc => (this.bingSearchBarContents = mapLoc), + { fireImmediately: true } + ); + this._disposers.highlight = reaction( () => this.allMapPushpins.map(doc => doc[Highlight]), - () => - this.allMapPushpins - .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })) - .filter(pair => pair.pushpin) - .forEach(({ doc, pushpin }) => { - if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { - this.recolorPin(pushpin, 'orange'); - this.map_pinHighlighted.set(pushpin, true); - } else if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { - this.recolorPin(pushpin); - this.map_pinHighlighted.delete(pushpin); - } - }), + () => { + const allPins = this.allMapPushpins.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin); + allPins.forEach(({ doc, pushpin }) => { + if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin); + this.map_pinHighlighted.delete(pushpin); + } + }); + allPins.forEach(({ doc, pushpin }) => { + if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) { + this.recolorPin(pushpin, 'orange'); + this.map_pinHighlighted.set(pushpin, true); + } + }); + }, { fireImmediately: true } ); - // this.updateMapType(); - this._disposer.location = reaction( - () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }), + + this._disposers.location = reaction( + () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }), locationObject => { // if (this._bingMap.current) try { @@ -862,43 +624,50 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); }; - // Keeps track of when dragging a pin onto map - draggingPin = false; dragToggle = (e: React.PointerEvent) => { - // console.log('DRAGGING TOGGLE'); - document.addEventListener('drop', this.dropPin, true); - document.addEventListener('pointermove', this.pinMove, true); - e.stopPropagation(); - }; - pinMove = (e: PointerEvent) => { - // console.log('MOVING'); - e.stopPropagation(); - }; - dropPin = (e: DragEvent) => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener('drop', this.dropPin, true); - document.removeEventListener('pointermove', this.pinMove, true); - let target = document.elementFromPoint(e.x, e.y); + let dragClone: HTMLDivElement | undefined; - while (target != null) { - if (target === this._ref.current) { - const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); - const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2; - const y = cpt[1] - 32 /* 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; + setupMoveUpEvents( + e, + e, + e => { + if (!dragClone) { + dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; + dragClone.style.position = 'absolute'; + dragClone.style.zIndex = '10000'; + DragManager.Root().appendChild(dragClone); + } + dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`; + return false; + }, + e => { + if (!dragClone) return; + DragManager.Root().removeChild(dragClone); + let target = document.elementFromPoint(e.x, e.y); + while (target) { + if (target === this._ref.current) { + const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); + const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2; + const y = cpt[1] - 32 /* 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.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map); + if (this.bingSearchBarContents) { + this.bingSearch().then(createPin); + } else createPin(); } - target = target.parentElement; - } + ); }; - searchbarKeyDown = (e: any) => { - if (e.key === 'Enter') { - this.bingSearch(); - } - }; + searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch(); + + _dragRef = React.createRef<HTMLDivElement>(); render() { const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -908,26 +677,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps onWheel={e => e.stopPropagation()} onPointerDown={async e => { e.button === 0 && !e.ctrlKey && e.stopPropagation(); - // just a simple test of bing maps geocode api - // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA'); - // this._bingMap.current.setView({ - // mapTypeId: this.MicrosoftMaps.MapTypeId.aerial, - // center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude), - // zoom: 15, - // }); }} - style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> + style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div> {renderAnnotations(this.opaqueFilter)} {SnappingManager.GetIsDragging() ? null : renderAnnotations()} - {this.annotationLayer} <div className="mapBox-searchbar"> <EditableText // editing setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)} onEnter={e => this.bingSearch()} - placeholder={this.bingSearchBarContents} + placeholder={this.bingSearchBarContents || 'enter city/zip/...'} textAlign="center" /> <IconButton @@ -939,41 +700,34 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </svg> } onClick={this.bingSearch} - onDoubleClick={function noRefCheck() {}} - onPointerDown={function noRefCheck() {}} - onPointerDownCapture={function noRefCheck() {}} - onPointerMove={function noRefCheck() {}} - onPointerMoveCapture={function noRefCheck() {}} - onPointerUp={function noRefCheck() {}} type={Type.TERT} /> - <div draggable={true} style={{ width: 30, height: 30 }} onPointerDown={this.dragToggle}> + <div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}> <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} /> </div> </div> <BingMapsReact - onMapReady={this.bingMapReady} + onMapReady={this.bingMapReady} // bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions} - onPointerUp - {...() => (this.draggingPin = false)}></BingMapsReact> + /> <div> {!this._mapReady ? null : this.allMapPushpins.map(pushpin => ( <DocumentView {...this.props} + renderDepth={this.props.renderDepth + 1} Document={pushpin} DataDoc={undefined} PanelWidth={returnOne} PanelHeight={returnOne} NativeWidth={returnOne} NativeHeight={returnOne} - onClick={() => new ScriptField(undefined)} onKey={undefined} onDoubleClick={undefined} onBrowseClick={undefined} @@ -983,7 +737,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps isDocumentActive={returnFalse} isContentActive={returnFalse} addDocTab={returnFalse} - ScreenToLocalTransform={() => new Transform(0, 0, 0)} + ScreenToLocalTransform={Transform.Identity} fitContentsToBox={undefined} focus={returnOne} /> @@ -1019,7 +773,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps )} </div> {/* </LoadScript > */} - <div className="mapBox-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> + <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> <SidebarAnnos ref={this._sidebarRef} {...this.props} diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index f42dec12c..a54bdcd5e 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -260,7 +260,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // } else { // alert("Your geolocation is not supported by browser.") // }; - map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); + map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5)); map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); setTimeout(() => { if (this._loadPending && this._map.getBounds()) { @@ -292,7 +292,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this._loadPending = false; this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map); } - this.dataDoc.mapZoom = this._map.getZoom(); + this.dataDoc.map_zoom = this._map.getZoom(); }; /** diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index afd9bccab..6ff22e929 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -471,12 +471,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { Doc.SetInPlace(bestTarget, 'longitude', NumCast(activeItem.config_longitude), true); changed = true; } - if (bestTarget.zoom !== activeItem.config_mapZoom) { - Doc.SetInPlace(bestTarget, 'mapZoom', NumCast(activeItem.config_mapZoom), true); + if (bestTarget.zoom !== activeItem.config_map_zoom) { + Doc.SetInPlace(bestTarget, 'map_zoom', NumCast(activeItem.config_map_zoom), true); changed = true; } - if (bestTarget.mapType !== activeItem.config_mapType) { - Doc.SetInPlace(bestTarget, 'mapType', StrCast(activeItem.config_mapType), true); + if (bestTarget.map_type !== activeItem.config_map_type) { + Doc.SetInPlace(bestTarget, 'map_type', StrCast(activeItem.config_map_type), true); + changed = true; + } + if (bestTarget.map !== activeItem.config_map) { + Doc.SetInPlace(bestTarget, 'map', StrCast(activeItem.config_map), true); changed = true; } } @@ -663,8 +667,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (pinProps.pinData.map) { // pinDoc.config_latitude = targetDoc?.latitude; // pinDoc.config_longitude = targetDoc?.longitude; - pinDoc.config_mapZoom = targetDoc?.mapZoom; - pinDoc.config_mapType = targetDoc?.mapType; + pinDoc.config_map_zoom = targetDoc?.map_zoom; + pinDoc.config_map_type = targetDoc?.map_type; //... } if (pinProps.pinData.poslayoutview) |