import { Autocomplete, GoogleMap, GoogleMapProps, InfoBox, Marker } from '@react-google-maps/api'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as React from "react"; import { Doc, WidthSym } from '../../../../fields/Doc'; import { documentSchema } from '../../../../fields/documentSchemas'; import { makeInterface } from '../../../../fields/Schema'; import { NumCast } from '../../../../fields/Types'; import { setupMoveUpEvents, emptyFunction } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; import { undoBatch } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { SidebarAnnos } from '../../SidebarAnnos'; import { FieldView, FieldViewProps } from '../FieldView'; import "./MapBox.scss" type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); export type LocationData = { id?: number; pos?: { lat: number, lng: number }; }; const mapContainerStyle = { height: '100%', }; const defaultCenter = { lat: 38.685, lng: -115.234, }; @observer export class MapBox extends ViewBoxAnnotatableComponent, MapDocument>(MapDocument) { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; private _sidebarRef = React.createRef(); public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); } @observable private _map = null as unknown as google.maps.Map; @observable private selectedPlace: LocationData = null as any; @observable private markerMap = {}; @observable private center = defaultCenter; @observable private zoom = 2.5; @observable private clickedLatLng = null; @observable private infoWindowOpen = false; @observable private bounds = new window.google.maps.LatLngBounds(); @observable private inputRef = React.createRef(); @observable private buttonRef = React.createRef(); @observable private searchMarkers: google.maps.Marker[] = []; private options = { fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields strictBounds: false, types: ["establishment"], // type pf places, subject of change according to user need } as google.maps.places.AutocompleteOptions; @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, this.options); @observable private myPlaces = [ { id: 1, pos: { lat: 39.09366509575983, lng: -94.58751660204751 } }, { id: 2, pos: { lat: 41.82399, lng: -71.41283 } }, { id: 3, pos: { lat: 47.606214, lng: -122.33207 } }, ]; @action private setSearchBox = (searchBox: any) => { this.searchBox = searchBox; } @action private setButton = (button: any) => { this.buttonRef = button; this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.buttonRef.current!) } // iterate myPlaces to size, center, and zoom map to contain all markers private fitBounds = (map: google.maps.Map) => { console.log('map bound is:' + this.bounds); this.myPlaces.map(place => { this.bounds.extend(place.pos); return place.id; }); map.fitBounds(this.bounds) } // store a reference to google map instance; fit map bounds to contain all markers @action private loadHandler = (map: google.maps.Map) => { this._map = map; this.fitBounds(map); // //add a custom control for button add marker // //TODO: why this doesn't work // const google = window.google; // console.log("google window: " + google) // const controlButtonDiv = document.createElement('div'); // ReactDOM.render(, controlButtonDiv); // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlButtonDiv); } @action private markerClickHandler = (e: MouseEvent, place: LocationData) => { // set which place was clicked this.selectedPlace = place console.log(this.selectedPlace.id); console.log(this.selectedPlace.pos); // used so clicking a second marker works if (this.infoWindowOpen) { this.infoWindowOpen = false; console.log("closeinfowindow") } else { this.infoWindowOpen = true; console.log("open infowindow") } } sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); } sidebarBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e, down, delta) => { const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]); const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; if (ratio >= 1) { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } return false; }, emptyFunction, this.toggleSidebar); } toggleSidebar = action(() => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; }); sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); @action @undoBatch private handleDragMarker = (marker: any, place: LocationData) => { // if (marker != null) { // place = { // id: place.id, // position: { // lat: marker.latLng.lat().toFixed(3), // lng: marker.latLng.lng().toFixed(3) // } // } // console.log(place); // console.log(this.myPlaces); // } } @action private handlePlaceChanged = () => { console.log(this.searchBox); 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) { console.log(this._map); this._map.fitBounds(place.geometry.viewport); } else { console.log(this._map); this._map.setCenter(place.geometry.location); this._map.setZoom(17); } /** * customize icon => customized icon for the nature of the location selected (hard to tell from other pre-existing markers, probably won't implement */ // 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), // }; this.searchMarkers.forEach((marker) => { marker.setMap(null); }); this.searchMarkers = []; this.searchMarkers.push( new window.google.maps.Marker({ map: this._map, title: place.name, position: place.geometry.location, }) ) } // @computed get markerContent() { // const allMarkers = this.childLayoutPairs // const markerId = NumCast(this.layoutDoc._itemIndex); // const selectedMarker = this.childLayoutPairs?.[markerId]; // const position = { // lat: NumCast(this.layoutDoc.lat), // lng: NumCast(this.layoutDoc.lng) // } // return <> // { // allMarkers?.map(place => ( // this.markerClickHandler(e, place.layout)} //?? // draggable={true} // onDragEnd={marker => this.handleDragMarker(marker, place.layout)} // /> // )) // } // {this.infoWindowOpen && selectedMarker && ( // //
//
// {/* the linkmenu as the ones in other nodes */} // // // //
// //
//
//
//
// )} // // } @action private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => { new window.google.maps.Marker({ position: location, map: map }); } // @action // private renderMarkerToMap = (marker: Doc) => { // const id = StrCast(marker.id); // const lat = NumCast(marker.lat); // const lng = NumCast(marker.lng); // return this.markerClickHandler(e, marker)} // /> // } render() { const { Document, fieldKey, isContentActive: active } = this.props; return
HELLO WORLD! {/*
e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > // {/*
this.loadHandler(map)} >
Add Marker
{this.myPlaces.map(place => ( this.markerLoadHandler(marker, place)} onClick={e => this.markerClickHandler(e, place)} draggable={true} onDragEnd={marker => this.handleDragMarker(marker, place)} /> ))} {this.infoWindowOpen && this.selectedPlace && ( )}
{/*
*/}
; } }