From df83cc8ae5e4ca5db2c1ca79b0017263f83ef496 Mon Sep 17 00:00:00 2001 From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:46:48 -0700 Subject: marker updates --- .../collections/MapView/CollectionMapView.scss | 1 + .../collections/MapView/CollectionMapView.tsx | 9 +- src/client/views/nodes/MapBox/MapBox.tsx | 293 +++++++++------------ src/client/views/nodes/MapBox/MapMarker.tsx | 115 ++++++++ src/client/views/nodes/MapMarker/MapMarker.tsx | 98 ------- 5 files changed, 239 insertions(+), 277 deletions(-) create mode 100644 src/client/views/nodes/MapBox/MapMarker.tsx delete mode 100644 src/client/views/nodes/MapMarker/MapMarker.tsx (limited to 'src') diff --git a/src/client/views/collections/MapView/CollectionMapView.scss b/src/client/views/collections/MapView/CollectionMapView.scss index 6ad0f3a92..0dc226c04 100644 --- a/src/client/views/collections/MapView/CollectionMapView.scss +++ b/src/client/views/collections/MapView/CollectionMapView.scss @@ -26,6 +26,7 @@ position: absolute; left: 50%; margin-left: -120px; + margin-top: 5px; } } } diff --git a/src/client/views/collections/MapView/CollectionMapView.tsx b/src/client/views/collections/MapView/CollectionMapView.tsx index d7025f6da..1166de61c 100644 --- a/src/client/views/collections/MapView/CollectionMapView.tsx +++ b/src/client/views/collections/MapView/CollectionMapView.tsx @@ -14,7 +14,7 @@ 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'; +import { MapMarker } from '../../nodes/MapBox/MapMarker'; /** @@ -60,8 +60,9 @@ const drawingManager = new google.maps.drawing.DrawingManager({ position: google.maps.ControlPosition.TOP_RIGHT, drawingModes: [ google.maps.drawing.OverlayType.MARKER, - google.maps.drawing.OverlayType.CIRCLE, - google.maps.drawing.OverlayType.POLYLINE, + // 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, ], }, }); @@ -83,11 +84,9 @@ export default class CollectionMapView extends CollectionSubView(); - @observable private buttonRef = React.createRef(); @observable private searchMarkers: google.maps.Marker[] = []; @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index cc7bf6d84..d52b91908 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,25 +1,35 @@ -import { Autocomplete, GoogleMap, GoogleMapProps, InfoBox, Marker } from '@react-google-maps/api'; +import { Autocomplete, GoogleMap, GoogleMapProps, InfoBox, InfoWindow, 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 { Cast, NumCast, StrCast } from '../../../../fields/Types'; import { setupMoveUpEvents, emptyFunction } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; import { undoBatch } from '../../../util/UndoManager'; +import { CollectionViewType } from '../../collections/CollectionView'; +import { TabDocView } from '../../collections/TabDocView'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { SidebarAnnos } from '../../SidebarAnnos'; +import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '../FieldView'; +import { PresMovement } from '../PresBox'; import "./MapBox.scss" +import { MapMarker } from './MapMarker'; type MapDocument = makeInterface<[typeof documentSchema]>; const MapDocument = makeInterface(documentSchema); +export type Coordinates = { + lat: number, + lng: number, +} + export type LocationData = { - id?: number; - pos?: { lat: number, lng: number }; + id: string; + pos: Coordinates; }; const mapContainerStyle = { @@ -31,56 +41,64 @@ const defaultCenter = { lng: -115.234, }; +const mapOptions = { + fullscreenControl: false, +} + +const drawingManager = new 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, + ], + }, +}); + +const 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; + @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 _map: google.maps.Map = null as unknown as google.maps.Map; + @observable private selectedPlace: MapMarker | undefined; + @observable private markerMap: { [id: string]: google.maps.Marker } = {}; @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[] = []; + @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + @observable private childDocs: MapMarker[] = []; - 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); + static _canAnnotate = true; + static _hadSelection: boolean = false; + private _sidebarRef = React.createRef(); + private _ref: React.RefObject = React.createRef(); - @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 + // iterate childDocs 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; + this.childDocs.map(place => { + this.bounds.extend(place._latlngLocation); + return place._markerId; }); map.fitBounds(this.bounds) } @@ -89,40 +107,46 @@ export class MapBox extends ViewBoxAnnotatableComponent { this._map = map; + drawingManager.setMap(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 markerLoadHandler = (marker: google.maps.Marker, place: MapMarker) => { + place._markerId ? this.markerMap[place._markerId] = marker : null; } @action - private markerClickHandler = (e: MouseEvent, place: LocationData) => { + private markerClickHandler = (e: MouseEvent, place: any) => { // set which place was clicked - this.selectedPlace = place + this.selectedPlace = place; - console.log(this.selectedPlace.id); - console.log(this.selectedPlace.pos); + console.log(this.selectedPlace); // used so clicking a second marker works if (this.infoWindowOpen) { this.infoWindowOpen = false; console.log("closeinfowindow") } - else { - this.infoWindowOpen = true; - console.log("open infowindow") - } + this.infoWindowOpen = true; + console.log("open infowindow") } + /** + * Called when dragging documents into map sidebar + * @param doc + * @param sidebarKey + * @returns + */ sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); } + + /** + * What does this do exactly? How to operate on sidebar? + * @param e + */ 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]); @@ -137,6 +161,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; @@ -145,23 +170,10 @@ export class MapBox extends ViewBoxAnnotatableComponent !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); - // } + sidebarWidth = () => { + !this.layoutDoc._showSidebar ? 0 : + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); } @action @@ -185,17 +197,16 @@ export class MapBox extends ViewBoxAnnotatableComponent 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), - // }; + // 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); }); @@ -203,64 +214,21 @@ export class MapBox extends ViewBoxAnnotatableComponent - // { - // 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 */} - //
- // a link to another node - //
- //
- //
- // a link to another node - //
- //
- //
- // a link to another node - //
- //
- //
- // - //
- //
- //
- //
- // )} - - // - // } + @action + private handleInfoWindowClose = () => { + if (this.infoWindowOpen) { + this.infoWindowOpen = false; + } + this.infoWindowOpen = false; + this.selectedPlace = undefined; + } @action private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => { @@ -270,38 +238,26 @@ export class MapBox extends ViewBoxAnnotatableComponent { - // 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! - {/*
+ {/* HELLO WORLD! */} +
e.stopPropagation()} onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - // {/* + // > */}
this.loadHandler(map)} + options={mapOptions} > -
-
-
Add Marker
-
-
- - {this.myPlaces.map(place => ( + {this.childDocs.map(place => ( this.markerLoadHandler(marker, place)} + key={place._markerId} + position={place._latlngLocation} + onLoad={marker => this.markerLoadHandler(marker, place)} onClick={e => this.markerClickHandler(e, place)} - draggable={true} - onDragEnd={marker => this.handleDragMarker(marker, place)} /> ))} {this.infoWindowOpen && this.selectedPlace && ( -
-
- a link to another node +
+
+
+
+ +

-
- -
- +
+ +
-
+ )}
- {/*
+ {/* {/* */} -
*/} +
; } diff --git a/src/client/views/nodes/MapBox/MapMarker.tsx b/src/client/views/nodes/MapBox/MapMarker.tsx new file mode 100644 index 000000000..34057cf48 --- /dev/null +++ b/src/client/views/nodes/MapBox/MapMarker.tsx @@ -0,0 +1,115 @@ +import { action, computed, IReactionDisposer, observable } from "mobx"; +import { observer } from "mobx-react"; +import { Transaction } from "prosemirror-state"; +import { EditorView } from "prosemirror-view"; +import * as React from "react"; +import { Doc, DocListCast, Opt } from "../../../../fields/Doc"; +import { documentSchema } from "../../../../fields/documentSchemas"; +import { Id } from "../../../../fields/FieldSymbols"; +import { createSchema, makeInterface } from "../../../../fields/Schema"; +import { Cast, NumCast } from "../../../../fields/Types"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import { DragManager } from "../../../util/DragManager"; +import { CollectionViewType } from "../../collections/CollectionView"; +import { TabDocView } from "../../collections/TabDocView"; +import { ViewBoxAnnotatableProps, ViewBoxAnnotatableComponent } from "../../DocComponent"; +import { AnchorMenu } from "../../pdf/AnchorMenu"; +import { FieldView, FieldViewProps } from "../FieldView"; +import { FormattedTextBox } from "../formattedText/FormattedTextBox"; +import { RichTextMenu } from "../formattedText/RichTextMenu"; +import { PresMovement } from "../PresBox"; + +type MarkerDocument = makeInterface<[typeof documentSchema]>; +const MarkerDocument = makeInterface(documentSchema); + +export type Coordinates = { + lat: number, + lng: number, +} + +@observer +export class MapMarker extends ViewBoxAnnotatableComponent(MarkerDocument) { + makeLinkAnchor(arg1: string, undefined: undefined, arg3: string) { + throw new Error("Method not implemented."); + } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapMarker, fieldKey); } + private _markerRef: React.RefObject = React.createRef(); + private _disposers: { [name: string]: IReactionDisposer } = {}; + _latlngLocation!: Coordinates; + _markerId!: number; + private _editorView: Opt // we'll see if this becomes useful for marker annotation/create link + @observable _title: string = ""; // the title of the marker + @observable _description: string = ""; // the description of the marker contents + @observable isMarkerActive: boolean = false; // whether the marker is selected (we'll see if we need this) + @observable activeLinks: Doc[] = []; //TBD: what linking data structure looks like + @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } // a list of documents with the same/similar geographic coordinates + @computed get tagDocs() { // might come in handy for filtering + const tagDocs: Doc[] = []; + for (const doc of this.childDocs) { + const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); + tagDocs.push(tagDoc); + } + return tagDocs; + } + + + /** + * Methods + */ + componentDidMount() { } + + componentWillMount() { } + + @computed private get filterAssociatedDocs() { + return + } + + addLinkToMarker = () => { } + + + + @action + setupAnchorMenu = () => { + AnchorMenu.Instance.Status = "marquee"; + AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { + this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); + return undefined; + }); + /** + * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. + * It also initiates a Drag/Drop interaction to place the text annotation. + */ + AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { + e.preventDefault(); + e.stopPropagation(); + const targetCreator = (annotationOn?: Doc) => { + const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn); + FormattedTextBox.SelectOnLoad = target[Id]; + return target; + }; + + // DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY); + }); + const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); + this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); + } + + // will see if end up using this + dispatchTransaction = (tx: Transaction) => { } + + + //will see if needed + // for inserting timestamps + insertTime = () => { } + + //for setting the title of the marker + @action + private updateTitle = () => { } + + //for updating the description of the marker + @action + private updateDescrption = () => { } + + + +} \ No newline at end of file diff --git a/src/client/views/nodes/MapMarker/MapMarker.tsx b/src/client/views/nodes/MapMarker/MapMarker.tsx deleted file mode 100644 index d87fd1b11..000000000 --- a/src/client/views/nodes/MapMarker/MapMarker.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { action, computed, IReactionDisposer, observable } from "mobx"; -import { observer } from "mobx-react"; -import { Transaction } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import * as React from "react"; -import { Doc, DocListCast, Opt } from "../../../../fields/Doc"; -import { documentSchema } from "../../../../fields/documentSchemas"; -import { Id } from "../../../../fields/FieldSymbols"; -import { createSchema, makeInterface } from "../../../../fields/Schema"; -import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; -import { DragManager } from "../../../util/DragManager"; -import { ViewBoxAnnotatableProps, ViewBoxAnnotatableComponent } from "../../DocComponent"; -import { AnchorMenu } from "../../pdf/AnchorMenu"; -import { FieldView, FieldViewProps } from "../FieldView"; -import { FormattedTextBox } from "../formattedText/FormattedTextBox"; -import { RichTextMenu } from "../formattedText/RichTextMenu"; - -type MarkerDocument = makeInterface<[typeof documentSchema]>; -const MarkerDocument = makeInterface(documentSchema); - -@observer -export class MapMarker extends ViewBoxAnnotatableComponent(MarkerDocument) { - makeLinkAnchor(arg1: string, undefined: undefined, arg3: string) { - throw new Error("Method not implemented."); - } - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapMarker, fieldKey); } - private _markerRef: React.RefObject = React.createRef(); - private _disposers: { [name: string]: IReactionDisposer } = {}; - private _latlngLocation!: google.maps.LatLng; - private _markerId!: number; - private _editorView: Opt // we'll see if this becomes useful for marker annotation/create link - @observable _title: string = ""; // the title of the marker - @observable _description: string = ""; // the description of the marker contents - @observable isMarkerActive: boolean = false; // whether the marker is selected (we'll see if we need this) - @observable associatedDocs: Doc[] = []; // a list of documents with the same/similar geographic coordinates - @observable activeLinks: Doc[] = []; //TBD: what linking data structure looks like - - /** - * Methods - */ - - componentDidMount() { } - - componentWillMount() { } - - @computed private get filterAssociatedDocs() { - return - } - - addLinkToMarker = () => { } - - - - @action - setupAnchorMenu = () => { - AnchorMenu.Instance.Status = "marquee"; - AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { - this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); - return undefined; - }); - /** - * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. - * It also initiates a Drag/Drop interaction to place the text annotation. - */ - AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { - e.preventDefault(); - e.stopPropagation(); - const targetCreator = (annotationOn?: Doc) => { - const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn); - FormattedTextBox.SelectOnLoad = target[Id]; - return target; - }; - - // DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docViewPath().lastElement(), this.getAnchor, targetCreator), e.pageX, e.pageY); - }); - const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); - this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); - } - - // will see if end up using this - dispatchTransaction = (tx: Transaction) => { } - - - //will see if needed - // for inserting timestamps - insertTime = () => { } - - //for setting the title of the marker - @action - private updateTitle = () => { } - - //for updating the description of the marker - @action - private updateDescrption = () => { } - - - -} \ No newline at end of file -- cgit v1.2.3-70-g09d2