From 992b5ca20414c28eba255cf319eb2b762cb69933 Mon Sep 17 00:00:00 2001 From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> Date: Tue, 13 Jul 2021 11:05:23 -0700 Subject: npm, sharp, npm-gyp issue, try relaunch --- .../collections/MapView/CollectionMapView.tsx | 309 ++++++++++++--------- src/client/views/nodes/MapMarker/MapMarker.tsx | 23 ++ 2 files changed, 207 insertions(+), 125 deletions(-) create mode 100644 src/client/views/nodes/MapMarker/MapMarker.tsx (limited to 'src') diff --git a/src/client/views/collections/MapView/CollectionMapView.tsx b/src/client/views/collections/MapView/CollectionMapView.tsx index 4243b89a5..ebf57c0c1 100644 --- a/src/client/views/collections/MapView/CollectionMapView.tsx +++ b/src/client/views/collections/MapView/CollectionMapView.tsx @@ -1,5 +1,5 @@ -import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, DrawingManager } from '@react-google-maps/api'; -import { observable, action, computed, Lambda, runInAction } from "mobx"; +import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, Autocomplete } from '@react-google-maps/api'; +import { observable, action, computed, Lambda, runInAction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../../fields/Doc"; import { documentSchema } from "../../../../fields/documentSchemas"; @@ -13,8 +13,21 @@ import { CollectionSubView } from "../CollectionSubView"; import React = require("react"); import requestPromise = require("request-promise"); import ReactDOM from 'react-dom'; +import { DragManager } from '../../../util/DragManager'; +import { MapMarker } from '../../nodes/MapMarker/MapMarker'; +/** + * Idea behind storing a marker: + * 1. on the map api, have a button "add marker" that adds the marker on the map & store the marker as a node in the collection + * (but don't render the marker in the collection itself) + * 2. each marker, as a node, has the same feature as all other nodes for linking (the same way one could form a link between a node inside a child collection + * and a node outside the child collection) + * + * /util/LinkManager.ts -- link relations + * + */ + type MapSchema = makeInterface<[typeof documentSchema]>; const MapSchema = makeInterface(documentSchema); @@ -33,11 +46,13 @@ const defaultCenter = { }; @observer -export default class CollectionMapView extends CollectionSubView(MapSchema) { +export default class CollectionMapView extends CollectionSubView>(MapSchema) { + private _dropDisposer?: DragManager.DragDropDisposer; + private _disposers: { [name: string]: IReactionDisposer } = {}; + - @observable private _map = null as any - @observable private mapRef = null as any; - @observable private selectedPlace: LocationData = { id: undefined, pos: undefined }; + @observable private _map = null as unknown as google.maps.Map; + @observable private selectedPlace: Doc = ; @observable private markerMap = {}; @observable private center = defaultCenter; @observable private zoom = 2.5; @@ -45,14 +60,21 @@ export default class CollectionMapView extends CollectionSubView(); - @observable private searchBox = new window.google.maps.places.SearchBox(this.inputRef.current!); + @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 } }, - { id: 4, pos: { lat: 36.7783, lng: 119.4179 } } ]; @action @@ -60,8 +82,14 @@ export default class CollectionMapView extends CollectionSubView { + 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: any) => { + private fitBounds = (map: google.maps.Map) => { console.log('map bound is:' + this.bounds); this.myPlaces.map(place => { this.bounds.extend(place.pos); @@ -72,8 +100,8 @@ export default class CollectionMapView extends CollectionSubView { - this.mapRef = map; + private loadHandler = (map: google.maps.Map) => { + this._map = map; this.fitBounds(map); // //add a custom control for button add marker @@ -86,7 +114,7 @@ export default class CollectionMapView extends CollectionSubView { + private markerClickHandler = (e: MouseEvent, place: Doc) => { // set which place was clicked this.selectedPlace = place @@ -96,20 +124,23 @@ export default class CollectionMapView extends CollectionSubView { - if (this.infoWindowOpen) { - this.infoWindowOpen = false; + else { + this.infoWindowOpen = true; + console.log("open infowindow") } - this.infoWindowOpen = false; - this.selectedPlace = { id: undefined, pos: undefined }; } + // @action + // private handleInfoWindowClose = () => { + // if (this.infoWindowOpen) { + // this.infoWindowOpen = false; + // } + // this.infoWindowOpen = false; + // this.selectedPlace = { id: undefined, pos: undefined }; + // } + // @action // private markerLoadHandler = (marker: any, place: LocationData) => { // if (marker != null) { @@ -119,118 +150,140 @@ export default class CollectionMapView extends CollectionSubView { - if (marker != null) { - place = { - id: place.id, - pos: { - lat: marker.latLng.lat().toFixed(3), - lng: marker.latLng.lng().toFixed(3) - } - } - - console.log(place); - console.log(this.myPlaces); - } + private handleDragMarker = (marker: any, place: Doc) => { + // 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); + // } } - // private mapRef = document.getElementById("map") - // private markers: google.maps.Marker[] = []; - - - // @action - // private setInputRef = (element: HTMLInputElement) => { - // if (this.inputRef.current) { - // this.inputRef! = element; - // console.log("htmlinputelement is:" + this.inputRef) - // } - // } - - // @observable private searchBox = new window.google.maps.places.SearchBox(this.inputRef.current!); - @action - private handlePlacesChanged = () => { + private handlePlaceChanged = () => { console.log(this.searchBox); - const places = this.searchBox.getPlaces(); + 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; + } - console.log(places); + // 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); + } - if (places.length == 0) { return; } + /** + * 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), + // }; - // clear out old markers this.searchMarkers.forEach((marker) => { marker.setMap(null); }); - this.searchMarkers = [] - - // for each place, get icon, name, location - places.forEach((place) => { - if (!place.geometry || !place.geometry.location) { - console.log("Returned place contains no geometry"); - return; - } - - 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 = []; + this.searchMarkers.push( + new window.google.maps.Marker({ + map: this._map, + title: place.name, + position: place.geometry.location, + }) + ) + } - //create a marker for each place - this.searchMarkers.push( - new google.maps.Marker({ - map: this._map, - icon: icon, - title: place.name, - position: place.geometry.location, - }) - ); - - if (place.geometry.viewport) { - // only geocodes have viewport - this.bounds.union(place.geometry.viewport); - } - else { - this.bounds.extend(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)} + /> + )) } - }); - - console.log(this.searchMarkers); + {this.infoWindowOpen && selectedMarker && ( + +
+
+ {/* the linkmenu as the ones in other nodes */} + + + +
+ +
+
+
+
+ )} + + } - private onBoundsChanged = () => { - this.searchBox.setBounds(this.bounds); + @action + private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => { + new window.google.maps.Marker({ + position: location, + map: map + }); } - private onCenterChanged = () => { - + @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)} + /> } - // private setBounds = () => { - // const places = this.searchBox.getPlaces(); - // if (places.length == 0) { return; } - - // const bounds = new google.maps.LatLngBounds(); - // places.forEach((place) => { - // if (!place.geometry || !place.geometry.location) { - // console.log("Returned place contains no geometry"); - // return; - // } - - // if (place.geometry.viewport) { - // // only geocodes have viewport - // bounds.union(place.geometry.viewport); - // } - // else { - // bounds.extend(place.geometry.location); - // } - // }); - - // return bounds - // } - render() { const { Document, fieldKey, isContentActive: active } = this.props; @@ -246,19 +299,22 @@ export default class CollectionMapView extends CollectionSubView */}
this._map = map} mapContainerStyle={mapContainerStyle} zoom={this.zoom} center={this.center} onLoad={map => this.loadHandler(map)} - onBoundsChanged={this.onBoundsChanged} - onCenterChanged={this.onCenterChanged} > - + onPlaceChanged={this.handlePlaceChanged}> - + + +
+
+
Add Marker
+
+
{this.myPlaces.map(place => ( ))} {this.infoWindowOpen && this.selectedPlace && ( - @@ -281,19 +337,22 @@ export default class CollectionMapView extends CollectionSubView
- + )} diff --git a/src/client/views/nodes/MapMarker/MapMarker.tsx b/src/client/views/nodes/MapMarker/MapMarker.tsx new file mode 100644 index 000000000..9705986a8 --- /dev/null +++ b/src/client/views/nodes/MapMarker/MapMarker.tsx @@ -0,0 +1,23 @@ +//TODO: mock imagebox, create marker as a doc +import { IReactionDisposer } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { documentSchema } from "../../../../fields/documentSchemas"; +import { createSchema, makeInterface } from "../../../../fields/Schema"; +import { ViewBoxBaseComponent } from "../../DocComponent"; +import { FieldView, FieldViewProps } from "../FieldView"; + +export const markerSchema = createSchema({ + lat: "number", + lng: "number" +}); + +type MarkerDocument = makeInterface<[typeof markerSchema, typeof documentSchema]>; +const MarkerDocument = makeInterface(markerSchema, documentSchema); + +@observer +export class MapMarker extends ViewBoxBaseComponent(MarkerDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapMarker, fieldKey); } + private _markerRef: React.RefObject = React.createRef(); + private _disposers: { [name: string]: IReactionDisposer } = {}; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2