diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/MapBox/AnimationUtility.ts | 16 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox.tsx | 962 |
2 files changed, 421 insertions, 557 deletions
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts index a5cff4668..42dfa59b7 100644 --- a/src/client/views/nodes/MapBox/AnimationUtility.ts +++ b/src/client/views/nodes/MapBox/AnimationUtility.ts @@ -7,7 +7,7 @@ import * as turf from '@turf/turf'; import { Position } from '@turf/turf'; import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson'; import { observer } from 'mobx-react'; -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable, runInAction, makeObservable } from 'mobx'; export enum AnimationStatus { START = 'start', @@ -26,16 +26,16 @@ export class AnimationUtility { private ROUTE_COORDINATES: Position[] = []; @observable - private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>; + private PATH?: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = undefined; - private PATH_DISTANCE: number; + private PATH_DISTANCE: number = 0; private FLY_IN_START_PITCH = 40; - private FIRST_LNG_LAT: { lng: number; lat: number }; + private FIRST_LNG_LAT: { lng: number; lat: number } = { lng: 0, lat: 0 }; private START_ALTITUDE = 3_000_000; - private MAP_REF: MapRef | null; + private MAP_REF: MapRef | null = null; - @observable private isStreetViewAnimation: boolean; - @observable private animationSpeed: AnimationSpeed; + @observable private isStreetViewAnimation: boolean = false; + @observable private animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM; @observable private previousLngLat: { lng: number; lat: number }; @@ -174,6 +174,7 @@ export class AnimationUtility { }; constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed, terrainDisplayed: boolean, mapRef: MapRef | null) { + makeObservable(this); this.FIRST_LNG_LAT = firstLngLat; this.previousLngLat = firstLngLat; this.isStreetViewAnimation = isStreetViewAnimation; @@ -232,6 +233,7 @@ export class AnimationUtility { return; } + if (!this.PATH) return; // calculate the distance along the path based on the animationPhase const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry.coordinates; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 724c1a770..735c04c1d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -3,7 +3,7 @@ import BingMapsReact from 'bingmaps-react'; // import 'mapbox-gl/dist/mapbox-gl.css'; import { Button, EditableText, IconButton, Size, Type } from 'browndash-components'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS, autorun} from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS, autorun, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; @@ -28,36 +28,34 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { FormattedTextBox } from '../formattedText/FormattedTextBox'; import { PinProps, PresBox } from '../trails'; import { MapAnchorMenu } from './MapAnchorMenu'; -import * as HME from "h264-mp4-encoder"; -import {simd} from 'wasm-feature-detect'; -import { +import { Map as MapboxMap, MapRef, - Marker, - ControlPosition, - FullscreenControl, - MapProvider, - MarkerProps, - NavigationControl, - ScaleControl, - ViewState, + Marker, + ControlPosition, + FullscreenControl, + MapProvider, + MarkerProps, + NavigationControl, + ScaleControl, + ViewState, ViewStateChangeEvent, useControl, GeolocateControl, Popup, MapEvent, Source, - Layer} from 'react-map-gl'; -import MapboxGeocoder, {GeocoderOptions} from '@mapbox/mapbox-gl-geocoder'; -import debounce from 'debounce'; + Layer, +} from 'react-map-gl'; +import MapboxGeocoder, { GeocoderOptions } from '@mapbox/mapbox-gl-geocoder!'; import './MapBox.scss'; import { NumberLiteralType } from 'typescript'; // import { GeocoderControl } from './GeocoderControl'; -import mapboxgl, { LngLat, LngLatBoundsLike, LngLatLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl'; +import mapboxgl, { LngLat, LngLatBoundsLike, LngLatLike, MapLayerMouseEvent, MercatorCoordinate } from 'mapbox-gl!'; import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, MultiLineString, Position } from 'geojson'; import { MarkerEvent } from 'react-map-gl/dist/esm/types'; -import { MapboxApiUtility, TransportationType} from './MapboxApiUtility'; +import { MapboxApiUtility, TransportationType } from './MapboxApiUtility'; import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; @@ -65,10 +63,10 @@ import { IconLookup, faCircleXmark, faFileExport, faGear, faMinus, faPause, faPl import { MarkerIcons } from './MarkerIcons'; import { SettingsManager } from '../../../util/SettingsManager'; import * as turf from '@turf/turf'; -import * as d3 from "d3"; +import * as d3 from 'd3'; import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; -import { CirclePicker, ColorState } from 'react-color'; +import { CirclePicker, ColorResult } from 'react-color'; // amongus /** @@ -91,24 +89,24 @@ const MAPBOX_FORWARD_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/map const MAPBOX_REVERSE_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/'; type PopupInfo = { - longitude: number, - latitude: number, - title: string, - description: string -} + longitude: number; + latitude: number; + title: string; + description: string; +}; export type GeocoderControlProps = Omit<GeocoderOptions, 'accessToken' | 'mapboxgl' | 'marker'> & { - mapboxAccessToken: string, + mapboxAccessToken: string; marker?: Omit<MarkerProps, 'longitude' | 'latitude'>; position: ControlPosition; onResult: (...args: any[]) => void; -} +}; type MapMarker = { - longitude: number, - latitude: number -} + longitude: number; + latitude: number; +}; /** * Consider integrating later: allows for drawing, circling, making shapes on map @@ -138,6 +136,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps private _disposers: { [key: string]: IReactionDisposer } = {}; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void); + constructor(props: any) { + super(props); + makeObservable(this); + } + @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); @@ -157,7 +160,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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); + console.log('Animation phase', this.animationPhase); const startIndex = Math.floor(index); const endIndex = Math.ceil(index); let feature: Feature<Geometry, GeoJsonProperties>; @@ -173,7 +176,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps feature = { type: 'Feature', properties: { - 'routeTitle': StrCast(this.routeToAnimate.title) + routeTitle: StrCast(this.routeToAnimate.title), }, geometry: geometry, }; @@ -186,7 +189,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const interpolator = d3.interpolateArray(startCoord, endCoord); const interpolatedCoord = interpolator(fraction); - + const coordinates = originalCoordinates.slice(0, startIndex + 1).concat([interpolatedCoord]); geometry = { @@ -196,12 +199,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps feature = { type: 'Feature', properties: { - 'routeTitle': StrCast(this.routeToAnimate.title) + routeTitle: StrCast(this.routeToAnimate.title), }, geometry: geometry, }; } - + autorun(() => { const animationUtil = this.animationUtility; const concattedCoordinates = geometry.coordinates.concat(originalCoordinates.slice(endIndex)); @@ -210,28 +213,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps properties: {}, geometry: { type: 'LineString', - coordinates: concattedCoordinates - } - } - if (animationUtil){ - animationUtil.setPath(newFeature) + coordinates: concattedCoordinates, + }, + }; + if (animationUtil) { + animationUtil.setPath(newFeature); } - }) + }); return feature; } - console.log("ERROR"); + console.log('ERROR'); return { type: 'Feature', properties: {}, geometry: { type: 'LineString', - coordinates: [] + coordinates: [], }, }; } @computed get selectedRouteCoordinates(): Position[] { let coordinates: Position[] = []; - if (this.routeToAnimate?.routeCoordinates){ + if (this.routeToAnimate?.routeCoordinates) { coordinates = JSON.parse(StrCast(this.routeToAnimate.routeCoordinates)); } return coordinates; @@ -247,7 +250,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return { type: 'Feature', properties: { - 'routeTitle': routeDoc.title}, + routeTitle: routeDoc.title, + }, geometry: geometry, }; }); @@ -265,7 +269,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } @computed get sidebarColor() { - return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4')); + return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4')); } @computed get SidebarKey() { return this.fieldKey + '_sidebar'; @@ -273,11 +277,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps componentDidMount() { this._unmounting = false; - this.props.setContentView?.(this); - + this._props.setContentView?.(this); } - _unmounting = false; componentWillUnmount(): void { this._unmounting = true; @@ -340,9 +342,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps e, (e, down, delta) => runInAction(() => { - const localDelta = this.props + const localDelta = this._props .ScreenToLocalTransform() - .scale(this.props.NativeDimScaling?.() || 1) + .scale(this._props.NativeDimScaling?.() || 1) .transformDirection(delta[0], delta[1]); const fullWidth = NumCast(this.layoutDoc._width); const mapWidth = fullWidth - this.sidebarWidth(); @@ -361,7 +363,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') ); }; - sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth(); /** * Handles toggle of sidebar on click the little comment button @@ -373,8 +375,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps key="sidebar" title="Toggle Sidebar" style={{ - display: !this.props.isContentActive() ? 'none' : undefined, - top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5, + display: !this._props.isContentActive() ? 'none' : undefined, + top: StrCast(this.Document._layout_showTitle) === 'title' ? 20 : 5, backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, }} onPointerDown={this.sidebarBtnDown}> @@ -406,16 +408,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }); const targetCreator = (annotationOn: Doc | undefined) => { - const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); - FormattedTextBox.SelectOnLoad = target[Id]; + const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow'); + FormattedTextBox.SetSelectOnLoad(target); return target; }; - const docView = this.props.DocumentView?.(); + const docView = this._props.DocumentView?.(); docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { dragComplete: e => { if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document; + e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document; e.annoDragData.linkSourceDoc.followLinkZoom = false; } }, @@ -454,15 +456,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey); - pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none'); + 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.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop)); - transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter]; - opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter]; - infoWidth = () => this.props.PanelWidth() / 5; - infoHeight = () => this.props.PanelHeight() / 5; + 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)); + transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter]; + opaqueFilter = () => [...this._props.childFilters(), Utils.OpaqueBackgroundFilter]; + infoWidth = () => this._props.PanelWidth() / 5; + infoHeight = () => this._props.PanelHeight() / 5; anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; savedAnnotations = () => this._savedAnnotations; @@ -500,7 +502,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }; @observable - bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string) + bingSearchBarContents: any = this.Document.map; // For Bing Maps: The contents of the Bing search bar (string) geoDataRequestOptions = { entityType: 'PopulatedPlace', @@ -509,15 +511,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // The pin that is selected @observable selectedPinOrRoute: Doc | undefined; - @action deselectPinOrRoute = () => { if (this.selectedPinOrRoute) { // // Removes filter - // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove'); - // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove'); - // Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); - + // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove'); + // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove'); + // Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); // const temp = this.selectedPin; // if (!this._unmounting) { // this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp)); @@ -529,11 +529,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // } // this.map_docToPinMap.set(temp, newpin); // this.selectedPin = undefined; - // this.bingSearchBarContents = this.rootDoc.map; + // this.bingSearchBarContents = this.Document.map; } }; - getView = async (doc: Doc) => { if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(); return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); @@ -547,9 +546,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.selectedPinOrRoute = pinDoc; this.bingSearchBarContents = pinDoc.map; - // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match'); - // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match'); - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check'); + // 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'); @@ -559,9 +558,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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.props.ScreenToLocalTransform().inverse().transformPoint(x, y); + const x = point.x + (this._props.PanelWidth() - this.sidebarWidth()) / 2; + const y = point.y + this._props.PanelHeight() / 2 + 32; + const cpt = this._props.ScreenToLocalTransform().inverse().transformPoint(x, y); MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true); document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true); @@ -572,7 +571,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps */ @action mapOnClick = (e: { location: { latitude: any; longitude: any } }) => { - this.props.select(false); + this._props.select(false); this.deselectPinOrRoute(); }; /* @@ -618,8 +617,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => { /// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER const anchor = Docs.Create.ConfigDocument({ - title: 'MapAnchor:' + this.rootDoc.title, - text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.rootDoc.map) || 'map location', + title: 'MapAnchor:' + this.Document.title, + text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location', 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), @@ -627,15 +626,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps config_map: StrCast((existingPin ?? this.selectedPinOrRoute)?.map) || StrCast(this.dataDoc.map), layout_unrendered: true, mapPin: existingPin ?? this.selectedPinOrRoute, - annotationOn: this.rootDoc, + annotationOn: this.Document, }); if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); return anchor; } - return this.rootDoc; + return this.Document; }; map_docToPinMap = new Map<Doc, any>(); @@ -682,9 +681,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps deleteSelectedPinOrRoute = undoable(() => { if (this.selectedPinOrRoute) { // Removes filter - Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPinOrRoute.latitude, 'remove'); - Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPinOrRoute.longitude, 'remove'); - Doc.setDocFilter(this.rootDoc, 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); } @@ -692,8 +691,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); }, 'delete pin'); - - tryHideMapAnchorMenu = (e: PointerEvent) => { let target = document.elementFromPoint(e.x, e.y); while (target) { @@ -711,8 +708,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps centerOnSelectedPin = () => { 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) { // this.dataDoc.latitude = this.selectedPin.latitude; @@ -733,7 +730,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps mapTypeId: 'grayscale', }; - /** * Map options */ @@ -752,8 +748,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }, }; - - recolorPin = (pin: Doc, color?: string) => { // this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin)); // this.map_docToPinMap.delete(pin); @@ -780,7 +774,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change')); this._disposers.mapLocation = reaction( - () => this.rootDoc.map, + () => this.Document.map, mapLoc => (this.bingSearchBarContents = mapLoc), { fireImmediately: true } ); @@ -805,7 +799,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); this._disposers.location = reaction( - () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }), + () => ({ lat: this.Document.latitude, lng: this.Document.longitude, zoom: this.Document.map_zoom, mapType: this.Document.map_type }), locationObject => { // if (this._bingMap.current) try { @@ -829,25 +823,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps setupMoveUpEvents( e, e, - e => { // move event + e => { + // move event if (!dragClone) { - dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; // copy draggable pin + 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 + 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 + 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.props.ScreenToLocalTransform().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; + 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] - 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; @@ -856,7 +852,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } }, e => { - const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map); + const createPin = () => this.createPushpin(this.Document.latitude, this.Document.longitude, this.Document.map); if (this.bingSearchBarContents) { this.bingSearch().then(createPin); } else createPin(); @@ -864,7 +860,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ); }; - // incrementer: number = 0; + // incrementer: number = 0; /* * Creates Pushpin doc and adds it to the list of annotations */ @@ -877,13 +873,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps false, [], { - title: location ?? `lat=${NumCast(latitude)},lng=${NumCast(longitude)}`, - map: location, - description: "", + title: location ?? `lat=${NumCast(latitude)},lng=${NumCast(longitude)}`, + map: location, + description: '', wikiData: wikiData, markerType: 'MAP_PIN', - markerColor: '#ff5722' - }, + markerColor: '#ff5722', + } // { title: map ?? `lat=${latitude},lng=${longitude}`, map: map }, // ,'pushpinIDamongus'+ this.incrementer++ ); @@ -896,11 +892,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @action createMapRoute = undoable((coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => { - const mapRoute = Docs.Create.MapRouteDocument( - false, - [], - {title: `${origin} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates)}, - ); + const mapRoute = Docs.Create.MapRouteDocument(false, [], { title: `${origin} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates) }); this.addDocument(mapRoute, this.annotationKey); if (createPinForDestination) { this.createPushpin(destination.center[1], destination.center[0], destination.place_name); @@ -912,43 +904,29 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch(); - - - - - @observable featuresFromGeocodeResults: any[] = []; - @action addMarkerForFeature = (feature: any) => { const location = feature.place_name; - if (feature.center){ + if (feature.center) { const longitude = feature.center[0]; const latitude = feature.center[1]; const wikiData = feature.properties?.wikiData; - - this.createPushpin( - latitude, - longitude, - location, - wikiData - ) - - if (this._mapRef.current){ + + this.createPushpin(latitude, longitude, location, wikiData); + + if (this._mapRef.current) { this._mapRef.current.flyTo({ - center: feature.center + center: feature.center, }); } this.featuresFromGeocodeResults = []; - } else { // TODO: handle error } - } - - + }; /** * Makes a forward geocoding API call to Mapbox to retrieve locations based on the search input @@ -956,12 +934,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps */ handleSearchChange = async (searchText: string) => { const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText); - if (features && !this.isAnimating){ + if (features && !this.isAnimating) { runInAction(() => { - this.settingsOpen= false; + this.settingsOpen = false; this.featuresFromGeocodeResults = features; this.routeToAnimate = undefined; - }) + }); } // try { // const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`; @@ -971,34 +949,31 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // this.featuresFromGeocodeResults = data.features; // }) // } catch (error: any){ - // // TODO: handle error in better way + // // TODO: handle error in better way // console.log(error); // } - } + }; // @action // debouncedCall = React.useCallback(debounce(this.debouncedOnSearchBarChange, 300), []); - @action handleMapClick = (e: MapLayerMouseEvent) => { - if (this._mapRef.current){ - const features = this._mapRef.current.queryRenderedFeatures( - e.point, { - layers: ['map-routes-layer'] - } - ); + if (this._mapRef.current) { + const features = this._mapRef.current.queryRenderedFeatures(e.point, { + layers: ['map-routes-layer'], + }); console.error(features); if (features && features.length > 0 && features[0].properties && features[0].geometry) { const geometry = features[0].geometry as LineString; const routeTitle: string = features[0].properties['routeTitle']; - const routeDoc: Doc | undefined = this.allRoutes.find((routeDoc) => routeDoc.title === routeTitle); + const routeDoc: Doc | undefined = this.allRoutes.find(routeDoc => routeDoc.title === routeTitle); this.deselectPinOrRoute(); // TODO: Also deselect route if selected - if (routeDoc){ + if (routeDoc) { this.selectedPinOrRoute = routeDoc; - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapRoute=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check'); + Doc.setDocFilter(this.Document, LinkedTo, `mapRoute=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check'); - // TODO: Recolor route + // TODO: Recolor route MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute; MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; @@ -1008,9 +983,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapAnchorMenu.Instance.setRouteDoc(routeDoc); // TODO: Subject to change - MapAnchorMenu.Instance.setAllMapboxPins( - this.allAnnotations.filter(anno => !anno.layout_unrendered) - ) + MapAnchorMenu.Instance.setAllMapboxPins(this.allAnnotations.filter(anno => !anno.layout_unrendered)); MapAnchorMenu.Instance.DisplayRoute = this.displayRoute; MapAnchorMenu.Instance.HideRoute = this.hideRoute; @@ -1025,16 +998,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true); document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true); - } + } } } - } - + }; /** * Makes a reverse geocoding API call to retrieve features corresponding to a map click (based on longitude - * and latitude). Sets the search results accordingly. - * @param e + * and latitude). Sets the search results accordingly. + * @param e */ handleMapDblClick = async (e: MapLayerMouseEvent) => { e.preventDefault(); @@ -1043,13 +1015,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps const latitude: number = lngLat.lat; const features = await MapboxApiUtility.reverseGeocodeForFeatures(longitude, latitude); - if (features){ + if (features) { runInAction(() => { this.featuresFromGeocodeResults = features; - }) + }); } - // // REVERSE GEOCODE TO GET LOCATION DETAILS + // // REVERSE GEOCODE TO GET LOCATION DETAILS // try { // const url = MAPBOX_REVERSE_GEOCODE_BASE_URL + encodeURI(longitude.toString() + "," + latitude.toString()) + '.json' + // `?access_token=${MAPBOX_ACCESS_TOKEN}`; @@ -1063,9 +1035,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // // TODO: handle error in better way // console.log(error); // } - } + }; - @observable + @observable currentPopup: PopupInfo | undefined = undefined; @action @@ -1075,23 +1047,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.selectedPinOrRoute = pinDoc; // this.bingSearchBarContents = pinDoc.map; - // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match'); - // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match'); - Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPinOrRoute)}`, 'check'); + // 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'); // TODO: check this method - - + MapAnchorMenu.Instance.Delete = this.deleteSelectedPinOrRoute; MapAnchorMenu.Instance.Center = this.centerOnSelectedPin; MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation; MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag; - // pass in the pinDoc + // pass in the pinDoc MapAnchorMenu.Instance.setPinDoc(pinDoc); - MapAnchorMenu.Instance.setAllMapboxPins( - this.allAnnotations.filter(anno => !anno.layout_unrendered) - ) + MapAnchorMenu.Instance.setAllMapboxPins(this.allAnnotations.filter(anno => !anno.layout_unrendered)); MapAnchorMenu.Instance.DisplayRoute = this.displayRoute; MapAnchorMenu.Instance.HideRoute = this.hideRoute; @@ -1106,22 +1075,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true); - - // this._mapRef.current.flyTo({ - // center: [NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3] - // }) - + // this._mapRef.current.flyTo({ + // center: [NumCast(pinDoc.longitude), NumCast(pinDoc.latitude)-3] + // }) }; @observable temporaryRouteSource: FeatureCollection = { type: 'FeatureCollection', - features: [] - } + features: [], + }; @action displayRoute = (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => { - if (routeInfoMap){ + if (routeInfoMap) { const newTempRouteSource: FeatureCollection = { type: 'FeatureCollection', features: [ @@ -1130,16 +1097,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps properties: {}, geometry: { type: 'LineString', - coordinates: routeInfoMap[type].coordinates - } - } - ] - } - // TODO: Create pin for destination - // TODO: Fly to point where full route will be shown + coordinates: routeInfoMap[type].coordinates, + }, + }, + ], + }; + // TODO: Create pin for destination + // TODO: Fly to point where full route will be shown this.temporaryRouteSource = newTempRouteSource; } - } + }; @observable isAnimating: boolean = false; @@ -1147,10 +1114,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps @observable routeToAnimate: Doc | undefined = undefined; - @observable + @observable animationPhase: number = 0; - @observable + @observable finishedFlyTo: boolean = false; @action @@ -1158,73 +1125,71 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.animationPhase = newValue; }; - @observable + @observable frameId: number | null = null; @action setFrameId = (frameId: number) => { this.frameId = frameId; - } + }; - @observable + @observable animationUtility: AnimationUtility | null = null; @action setAnimationUtility = (util: AnimationUtility) => { this.animationUtility = util; - } + }; @action openAnimationPanel = (routeDoc: Doc | undefined) => { - if (routeDoc){ + if (routeDoc) { MapAnchorMenu.Instance.fadeOut(true); document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true); this.routeToAnimate = routeDoc; } - } + }; @observable mapboxMapViewState: ViewState = { zoom: this.dataDoc.map_zoom ? NumCast(this.dataDoc.map_zoom) : 8, - longitude: this.dataDoc.longitude ? NumCast(this.dataDoc.longitude) : -71.4128, - latitude: this.dataDoc.latitude ? NumCast(this.dataDoc.latitude) : 41.8240, + longitude: this.dataDoc.longitude ? NumCast(this.dataDoc.longitude) : -71.4128, + latitude: this.dataDoc.latitude ? NumCast(this.dataDoc.latitude) : 41.824, pitch: this.dataDoc.map_pitch ? NumCast(this.dataDoc.map_pitch) : 0, bearing: this.dataDoc.map_bearing ? NumCast(this.dataDoc.map_bearing) : 0, padding: { top: 0, bottom: 0, left: 0, - right: 0 + right: 0, }, - } + }; - @computed + @computed get preAnimationViewState() { - if (!this.isAnimating){ + if (!this.isAnimating) { return this.mapboxMapViewState; } } - - @observable + @observable isStreetViewAnimation: boolean = false; - @observable + @observable animationSpeed: AnimationSpeed = AnimationSpeed.MEDIUM; - @observable animationLineColor: string = '#ffff00'; @action - setAnimationLineColor = (color: ColorState) => { + setAnimationLineColor = (color: ColorResult) => { this.animationLineColor = color.hex; - } + }; @action updateAnimationSpeed = () => { let newAnimationSpeed: AnimationSpeed; - switch (this.animationSpeed){ + switch (this.animationSpeed) { case AnimationSpeed.SLOW: newAnimationSpeed = AnimationSpeed.MEDIUM; break; @@ -1239,63 +1204,62 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps break; } this.animationSpeed = newAnimationSpeed; - if (this.animationUtility){ + 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'; + case AnimationSpeed.SLOW: + return '1x speed'; + case AnimationSpeed.MEDIUM: + return '2x speed'; + case AnimationSpeed.FAST: + return '3x speed'; + default: + return '2x speed'; } } - @computed get animationSpeedIcon(): JSX.Element{ + @computed get animationSpeedIcon(): JSX.Element { switch (this.animationSpeed) { case AnimationSpeed.SLOW: - return slowSpeedIcon; + return slowSpeedIcon; case AnimationSpeed.MEDIUM: - return mediumSpeedIcon; + return mediumSpeedIcon; case AnimationSpeed.FAST: - return fastSpeedIcon; + return fastSpeedIcon; default: - return mediumSpeedIcon; - } + return mediumSpeedIcon; + } } @action toggleIsStreetViewAnimation = () => { const newVal = !this.isStreetViewAnimation; this.isStreetViewAnimation = newVal; - if (this.animationUtility){ - this.animationUtility.updateIsStreetViewAnimation(newVal) + if (this.animationUtility) { + this.animationUtility.updateIsStreetViewAnimation(newVal); } - } + }; - @observable + @observable dynamicRouteFeature: Feature<Geometry, GeoJsonProperties> = { type: 'Feature', properties: {}, geometry: { type: 'LineString', - coordinates: [] - } + coordinates: [], + }, }; - - @observable + @observable path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties> = { type: 'Feature', geometry: { type: 'LineString', - coordinates: [] + coordinates: [], }, - properties: {} + properties: {}, }; getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => { @@ -1306,14 +1270,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps return { type: 'Feature', properties: { - 'routeTitle': routeDoc.title}, + routeTitle: routeDoc.title, + }, geometry: geometry, }; - } + }; - @action + @action playAnimation = (status: AnimationStatus) => { - if (!this._mapRef.current || !this.routeToAnimate){ + if (!this._mapRef.current || !this.routeToAnimate) { return; } @@ -1322,43 +1287,32 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.finishedFlyTo = AnimationStatus.RESUME ? this.finishedFlyTo : false; const path = turf.lineString(this.selectedRouteCoordinates); - + this.settingsOpen = false; this.path = path; this.isAnimating = true; runInAction(() => { - return new Promise<void>(async (resolve) => { + return new Promise<void>(async resolve => { const targetLngLat = { lng: this.selectedRouteCoordinates[0][0], lat: this.selectedRouteCoordinates[0][1], }; - const animationUtil = new AnimationUtility( - targetLngLat, - this.selectedRouteCoordinates, - this.isStreetViewAnimation, - this.animationSpeed, - this.showTerrain, - this._mapRef.current - ); + 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 updateAnimationPhase = ( - newAnimationPhase: number, - ) => { + const updateAnimationPhase = (newAnimationPhase: number) => { this.setAnimationPhase(newAnimationPhase); }; - - if (status !== AnimationStatus.RESUME) { + if (status !== AnimationStatus.RESUME) { const result = await animationUtil.flyInAndRotate({ map: this._mapRef.current!, // targetLngLat, @@ -1372,18 +1326,17 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps updateFrameId, }); - console.log("Bearing: ", result.bearing); - console.log("Altitude: ", result.altitude); - - } + console.log('Bearing: ', result.bearing); + console.log('Altitude: ', result.altitude); + } 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({ - map: this._mapRef.current!, + map: this._mapRef.current!, // path: this.path, // startBearing: -20, // startAltitude: this.isStreetViewAnimation ? 80 : 12000, @@ -1392,43 +1345,39 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps updateAnimationPhase, updateFrameId, }); - + // get the bounds of the linestring, use fitBounds() to animate to a final view const bbox3d = turf.bbox(this.path); - + const bbox2d: LngLatBoundsLike = [bbox3d[0], bbox3d[1], bbox3d[2], bbox3d[3]]; - + this._mapRef.current!.fitBounds(bbox2d, { duration: 3000, pitch: 30, bearing: 0, padding: 120, }); - + setTimeout(() => { this.isStreetViewAnimation = false; resolve(); }, 10000); }); - - }) - - - } - + }); + }; - @action + @action pauseAnimation = () => { - if (this.frameId && this.animationPhase > 0){ + if (this.frameId && this.animationPhase > 0) { window.cancelAnimationFrame(this.frameId); this.frameId = null; this.isAnimating = false; } - } + }; @action stopAnimation = (close: boolean) => { - if (this.frameId){ + if (this.frameId) { window.cancelAnimationFrame(this.frameId); } this.animationPhase = 0; @@ -1441,22 +1390,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.routeToAnimate = undefined; this.animationUtility = null; } - if (this.preAnimationViewState){ + if (this.preAnimationViewState) { this.mapboxMapViewState = this.preAnimationViewState; } - - - } + }; @action - exportAnimationToVideo = () => { - - } + exportAnimationToVideo = () => {}; getRouteAnimationOptions = (): JSX.Element => { return ( <> - <IconButton + <IconButton tooltip={this.isAnimating && this.finishedFlyTo ? 'Pause Animation' : 'Play Animation'} onPointerDown={() => { if (this.isAnimating && this.finishedFlyTo) { @@ -1467,87 +1412,57 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.playAnimation(AnimationStatus.START); // Play from the beginning } }} - icon={this.isAnimating && this.finishedFlyTo ? - <FontAwesomeIcon icon={faPause as IconLookup}/> - : - <FontAwesomeIcon icon={faPlay as IconLookup}/> - } - color='black' - size={Size.MEDIUM} - /> - {this.isAnimating && this.finishedFlyTo && - <IconButton - tooltip='Restart animation' - onPointerDown={() => { - this.stopAnimation(false); - this.playAnimation(AnimationStatus.START) - }} - icon={<FontAwesomeIcon icon={faRotate as IconLookup}/>} - color='black' - size={Size.MEDIUM} - /> - - } - <IconButton - style={{marginRight: '10px'}} - tooltip='Stop and close animation' - onPointerDown={() => this.stopAnimation(true)} - icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup}/>} - color='black' + icon={this.isAnimating && this.finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />} + color="black" size={Size.MEDIUM} /> + {this.isAnimating && this.finishedFlyTo && ( + <IconButton + tooltip="Restart animation" + onPointerDown={() => { + this.stopAnimation(false); + this.playAnimation(AnimationStatus.START); + }} + icon={<FontAwesomeIcon icon={faRotate as IconLookup} />} + color="black" + size={Size.MEDIUM} + /> + )} + <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup} />} color="black" size={Size.MEDIUM} /> <> - <div className='animation-suboptions'> + <div className="animation-suboptions"> <div>|</div> <FormControlLabel - className='first-person-label' - style={{width: '130px'}} - 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} + className="first-person-label" + style={{ width: '130px' }} + label="1st person animation:" + labelPlacement="start" + control={<Checkbox color="success" checked={this.isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} /> - <div id='divider'>|</div> - <div style={{width: '230px'}}>Select Line Color: </div> - <CirclePicker - circleSize={12} - circleSpacing={5} - width='100%' - colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']} - onChange={(color) => this.setAnimationLineColor(color)} - /> + <div id="divider">|</div> + <IconButton tooltip={this.animationSpeedTooltipText} onPointerDown={this.updateAnimationSpeed} icon={this.animationSpeedIcon} size={Size.MEDIUM} /> + <div id="divider">|</div> + <div style={{ width: '230px' }}>Select Line Color: </div> + <CirclePicker circleSize={12} circleSpacing={5} width="100%" colors={['#ffff00', '#03a9f4', '#ff0000', '#ff5722', '#000000', '#673ab7']} onChange={color => this.setAnimationLineColor(color)} /> </div> - </> + </> </> - ) - } + ); + }; @action hideRoute = () => { this.temporaryRouteSource = { type: 'FeatureCollection', - features: [] - } - } - + features: [], + }; + }; - @observable + @observable settingsOpen: boolean = false; - @observable - mapStyle: string = 'mapbox://styles/mapbox/streets-v12' + @observable + mapStyle: string = 'mapbox://styles/mapbox/streets-v12'; @observable showTerrain: boolean = true; @@ -1558,103 +1473,94 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps this.featuresFromGeocodeResults = []; this.settingsOpen = !this.settingsOpen; } - } + }; @action changeMapStyle = (e: React.ChangeEvent<HTMLSelectElement>) => { this.dataDoc.map_style = `mapbox://styles/mapbox/${e.target.value}`; // this.mapStyle = `mapbox://styles/mapbox/${e.target.value}` - } + }; - @action + @action onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => { const bearing = parseInt(e.target.value); - if (!isNaN(bearing) && this._mapRef.current){ + if (!isNaN(bearing) && this._mapRef.current) { const fixedBearing = Math.max(0, Math.min(360, bearing)); this._mapRef.current.setBearing(fixedBearing); this.dataDoc.map_bearing = fixedBearing; this.mapboxMapViewState = { ...this.mapboxMapViewState, - bearing: fixedBearing - } + bearing: fixedBearing, + }; } - } + }; - @action + @action onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => { const pitch = parseInt(e.target.value); - if (!isNaN(pitch) && this._mapRef.current){ + if (!isNaN(pitch) && this._mapRef.current) { const fixedPitch = Math.max(0, Math.min(85, pitch)); this._mapRef.current.setPitch(fixedPitch); this.dataDoc.map_pitch = fixedPitch; this.mapboxMapViewState = { ...this.mapboxMapViewState, - pitch: fixedPitch - } + pitch: fixedPitch, + }; } - } + }; - @action + @action onZoomChange = (e: React.ChangeEvent<HTMLInputElement>) => { const zoom = parseInt(e.target.value); - if (!isNaN(zoom) && this._mapRef.current){ + if (!isNaN(zoom) && this._mapRef.current) { const fixedZoom = Math.max(0, Math.min(16, zoom)); this._mapRef.current.setZoom(fixedZoom); this.dataDoc.map_zoom = fixedZoom; this.mapboxMapViewState = { ...this.mapboxMapViewState, - zoom: fixedZoom - } + zoom: fixedZoom, + }; } - } + }; @action onStepZoomChange = (increment: boolean) => { if (this._mapRef.current) { let newZoom: number; if (increment) { - console.log('inc') + console.log('inc'); newZoom = this.mapboxMapViewState.zoom + 1; - } else { - console.log('dec') + console.log('dec'); newZoom = this.mapboxMapViewState.zoom - 1; } this._mapRef.current.setZoom(newZoom); this.dataDoc.map_zoom = newZoom; this.mapboxMapViewState = { ...this.mapboxMapViewState, - zoom: increment ? Math.min(16, newZoom) : Math.max(0, newZoom) - } + zoom: increment ? Math.min(16, newZoom) : Math.max(0, newZoom), + }; } - - } - + }; @action onMapMove = (e: ViewStateChangeEvent) => { this.mapboxMapViewState = e.viewState; this.dataDoc.longitude = e.viewState.longitude; this.dataDoc.latitude = e.viewState.latitude; - } + }; @action toggleShowTerrain = () => { this.showTerrain = !this.showTerrain; - } + }; getMarkerIcon = (pinDoc: Doc): JSX.Element | null => { const markerType = StrCast(pinDoc.markerType); const markerColor = StrCast(pinDoc.markerColor); return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null; - - } - - - - - + }; static _firstRender = true; static _rerenderDelay = 500; @@ -1662,7 +1568,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps 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.rootDoc[DocCss]; + this.Document[DocCss]; if (MapBox._rerenderDelay) { // prettier-ignore this._rerenderTimeout = this._rerenderTimeout ?? @@ -1671,12 +1577,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps MapBox._rerenderDelay = 0; } this._rerenderTimeout = undefined; - this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1; + this.Document[DocCss] = this.Document[DocCss] + 1; }), MapBox._rerenderDelay); return null; } - const scale = this.props.NativeDimScaling?.() || 1; + const scale = this._props.NativeDimScaling?.() || 1; const renderAnnotations = (childFilters?: () => string[]) => null; return ( @@ -1687,108 +1593,76 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps onPointerDown={async e => { e.button === 0 && !e.ctrlKey && e.stopPropagation(); }} - style={{ transformOrigin: "top left", transform: `scale(${scale})`, width: `${100 / scale}%`, height: `${100 / scale}%`, pointerEvents: this.pointerEvents() }}> + style={{ pointerEvents: this.pointerEvents() }}> <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div> {renderAnnotations(this.opaqueFilter)} - {SnappingManager.GetIsDragging() ? null : renderAnnotations()} - {!this.routeToAnimate && + {SnappingManager.IsDragging ? null : renderAnnotations()} + {!this.routeToAnimate && ( <div className="mapBox-searchbar"> - <TextField - fullWidth - placeholder='Enter a location' - onChange={(e) => 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 && - <div className='mapbox-settings-panel' style={{right: `${0+ this.sidebarWidth()}px`}}> - <div className='mapbox-style-select'> - <div> - Map Style: - </div> + <TextField fullWidth placeholder="Enter a location" 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 && ( + <div className="mapbox-settings-panel" style={{ right: `${0 + this.sidebarWidth()}px` }}> + <div className="mapbox-style-select"> + <div>Map Style:</div> <div> <select onChange={this.changeMapStyle}> - <option value='streets-v11'>Streets</option> - <option value='outdoors-v12'>Outdoors</option> - <option value='light-v11'>Light</option> - <option value='dark-v11'>Dark</option> - <option value='satellite-v9'>Satellite</option> - <option value='satellite-streets-v12'>Satellite Streets</option> - <option value='navigation-day-v1'>Navigation Day</option> - <option value='navigation-night-v1'>Navigation Night</option> + <option value="streets-v11">Streets</option> + <option value="outdoors-v12">Outdoors</option> + <option value="light-v11">Light</option> + <option value="dark-v11">Dark</option> + <option value="satellite-v9">Satellite</option> + <option value="satellite-streets-v12">Satellite Streets</option> + <option value="navigation-day-v1">Navigation Day</option> + <option value="navigation-night-v1">Navigation Night</option> </select> </div> </div> - <div className='mapbox-bearing-selection'> + <div className="mapbox-bearing-selection"> <div>Bearing: </div> - <input - value={NumCast(this.mapboxMapViewState.bearing).toFixed(2)} - type='number' - onChange={this.onBearingChange}/> + <input value={NumCast(this.mapboxMapViewState.bearing).toFixed(2)} type="number" onChange={this.onBearingChange} /> </div> - <div className='mapbox-pitch-selection'> + <div className="mapbox-pitch-selection"> <div>Pitch: </div> - <input - value={NumCast(this.mapboxMapViewState.pitch).toFixed(2)} - type='number' - onChange={this.onPitchChange}/> + <input value={NumCast(this.mapboxMapViewState.pitch).toFixed(2)} type="number" onChange={this.onPitchChange} /> </div> - <div className='mapbox-pitch-selection'> + <div className="mapbox-pitch-selection"> <div>Zoom: </div> - <input - value={NumCast(this.mapboxMapViewState.zoom).toFixed(2)} - type='number' - onChange={this.onZoomChange}/> + <input value={NumCast(this.mapboxMapViewState.zoom).toFixed(2)} type="number" onChange={this.onZoomChange} /> </div> - <div className='mapbox-terrain-selection'> + <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 && - <div className='animation-panel'> - <div id='route-to-animate-title'> - {StrCast(this.routeToAnimate.title)} - </div> - <div className='route-animation-options'> - {this.getRouteAnimationOptions()} - </div> + )} + {this.routeToAnimate && ( + <div className="animation-panel"> + <div id="route-to-animate-title">{StrCast(this.routeToAnimate.title)}</div> + <div className="route-animation-options">{this.getRouteAnimationOptions()}</div> </div> - } + )} {this.featuresFromGeocodeResults.length > 0 && ( - <div className='mapbox-geocoding-search-results'> + <div className="mapbox-geocoding-search-results"> <React.Fragment> <h4>Choose a location for your pin: </h4> {this.featuresFromGeocodeResults .filter(feature => feature.place_name) .map((feature, idx) => ( - <div - key={idx} - className='search-result-container' - onClick={() => { - this.handleSearchChange(""); - this.addMarkerForFeature(feature); - }} - > - <div className='search-result-place-name'> - {feature.place_name} + <div + key={idx} + className="search-result-container" + onClick={() => { + this.handleSearchChange(''); + this.addMarkerForFeature(feature); + }}> + <div className="search-result-place-name">{feature.place_name}</div> </div> - </div> - ))} + ))} </React.Fragment> - - </div> + </div> )} {/* <div className='zoom-box'> <IconButton // increment @@ -1810,112 +1684,94 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps ref={this._mapRef} mapboxAccessToken={MAPBOX_ACCESS_TOKEN} id="mapbox-map" + viewState={{ ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width) / scale, height: NumCast(this.layoutDoc._height) / scale }} mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'} - style={{height: NumCast(this.layoutDoc._height), width: NumCast(this.layoutDoc._width)}} initialViewState={this.isAnimating ? undefined : this.mapboxMapViewState} onMove={this.onMapMove} onClick={this.handleMapClick} onDblClick={this.handleMapDblClick} - 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='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) && + 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="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.isStreetViewAnimation && + {!this.isStreetViewAnimation && ( <> - <Source id='animated-route' type='geojson' data={this.updatedRouteCoordinates}/> - <Layer - id='dynamic-animation-line' - type='line' - source='animated-route' - paint={{ - 'line-color': this.animationLineColor, - 'line-width': 5, - }} + <Source id="animated-route" type="geojson" data={this.updatedRouteCoordinates} /> + <Layer + id="dynamic-animation-line" + type="line" + source="animated-route" + paint={{ + 'line-color': this.animationLineColor, + 'line-width': 5, + }} /> </> - } - <Source id='start-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.04)}/> - <Source id='start-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.25)}/> - <Source id='end-pin-base' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.04)}/> - <Source id='end-pin-top' type='geojson' data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.25)}/> - <Layer id='start-fill-pin-base' type='fill-extrusion' source='start-pin-base' + )} + <Source id="start-pin-base" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.04)} /> + <Source id="start-pin-top" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates[0], 0.25)} /> + <Source id="end-pin-base" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.04)} /> + <Source id="end-pin-top" type="geojson" data={AnimationUtility.createGeoJSONCircle(this.selectedRouteCoordinates.slice(-1)[0], 0.25)} /> + <Layer + id="start-fill-pin-base" + type="fill-extrusion" + source="start-pin-base" paint={{ 'fill-extrusion-color': '#0bfc03', - 'fill-extrusion-height': 1000 + 'fill-extrusion-height': 1000, }} /> - <Layer id='start-fill-pin-top' type='fill-extrusion' source='start-pin-top' + <Layer + id="start-fill-pin-top" + type="fill-extrusion" + source="start-pin-top" paint={{ 'fill-extrusion-color': '#0bfc03', 'fill-extrusion-base': 1000, - 'fill-extrusion-height': 1200 + 'fill-extrusion-height': 1200, }} /> - <Layer id='end-fill-pin-base' type='fill-extrusion' source='end-pin-base' + <Layer + id="end-fill-pin-base" + type="fill-extrusion" + source="end-pin-base" paint={{ 'fill-extrusion-color': '#eb1c1c', - 'fill-extrusion-height': 1000 + 'fill-extrusion-height': 1000, }} /> - <Layer id='end-fill-pin-top' type='fill-extrusion' source='end-pin-top' + <Layer + id="end-fill-pin-top" + type="fill-extrusion" + source="end-pin-top" paint={{ 'fill-extrusion-color': '#eb1c1c', 'fill-extrusion-base': 1000, - 'fill-extrusion-height': 1200 + 'fill-extrusion-height': 1200, }} /> - </> - } - + )} <> - {!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)} - </Marker> - ))} + {!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)} + </Marker> + ))} </> - + {/* {this.mapMarkers.length > 0 && this.mapMarkers.map((marker, idx) => ( <Marker key={idx} longitude={marker.longitude} latitude={marker.latitude}/> ))} */} - </MapboxMap> </MapProvider> @@ -1935,8 +1791,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps .map((pushpin, i) => ( <DocumentView key={i} - {...this.props} - renderDepth={this.props.renderDepth + 1} + {...this._props} + renderDepth={this._props.renderDepth + 1} Document={pushpin} DataDoc={undefined} PanelWidth={returnOne} @@ -1960,7 +1816,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps </div> */} {/* <MapBoxInfoWindow key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]} - {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} + {...OmitKeys(this._props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})} markerMap={this.markerMap} PanelWidth={this.infoWidth} @@ -1974,9 +1830,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> <SidebarAnnos ref={this._sidebarRef} - {...this.props} + {...this._props} fieldKey={this.fieldKey} - rootDoc={this.rootDoc} + Document={this.Document} layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} usePanelWidth={true} @@ -1995,7 +1851,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps } } -{/* <Autocomplete +{ + /* <Autocomplete fullWidth id="map-location-searcher" freeSolo @@ -2014,8 +1871,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps placeholder='Enter a location' /> )} - /> */} - {/* <EditableText + /> */ +} +{ + /* <EditableText // editing setVal={(newText: string | number) => typeof newText === 'string' && this.handleSearchChange(newText)} // onEnter={e => this.bingSearch()} @@ -2024,8 +1883,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // placeholder={this.bingSearchBarContents || 'Enter a location'} placeholder='Enter a location' textAlign="center" - /> */} - {/* <IconButton + /> */ +} +{ + /* <IconButton icon={ <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF"> <path @@ -2038,4 +1899,5 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps /> <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> */ +} |