aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/MapBox
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/MapBox')
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts508
-rw-r--r--src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx69
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx340
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss2
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx1194
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx185
-rw-r--r--src/client/views/nodes/MapBox/MapPushpinBox.tsx14
7 files changed, 1066 insertions, 1246 deletions
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 11b335a96..a5cff4668 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -1,12 +1,13 @@
-import mapboxgl from "mapbox-gl";
-import { MercatorCoordinate } from "mapbox-gl";
-import { MapRef } from "react-map-gl";
+import mapboxgl from 'mapbox-gl';
+import { MercatorCoordinate } from 'mapbox-gl';
+import { MapRef } from 'react-map-gl';
import * as React from 'react';
-import * as d3 from "d3";
+import * as d3 from 'd3';
import * as turf from '@turf/turf';
-import { Position } from "@turf/turf";
-import { Feature, GeoJsonProperties, Geometry } from "geojson";
-import { action, computed, observable, runInAction } from "mobx";
+import { Position } from '@turf/turf';
+import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
+import { observer } from 'mobx-react';
+import { action, computed, observable, runInAction } from 'mobx';
export enum AnimationStatus {
START = 'start',
@@ -21,15 +22,15 @@ export enum AnimationSpeed {
}
export class AnimationUtility {
- private SMOOTH_FACTOR = 0.95
- private ROUTE_COORDINATES: Position[] = [];
+ private SMOOTH_FACTOR = 0.95;
+ private ROUTE_COORDINATES: Position[] = [];
@observable
private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
-
+
private PATH_DISTANCE: number;
private FLY_IN_START_PITCH = 40;
- private FIRST_LNG_LAT: {lng: number, lat: number};
+ private FIRST_LNG_LAT: { lng: number; lat: number };
private START_ALTITUDE = 3_000_000;
private MAP_REF: MapRef | null;
@@ -37,27 +38,27 @@ export class AnimationUtility {
@observable private animationSpeed: AnimationSpeed;
@observable
- private previousLngLat: {lng: number, lat: number};
+ private previousLngLat: { lng: number; lat: number };
private previousAltitude: number | null = null;
private previousPitch: number | null = null;
-
+
private currentStreetViewBearing: number = 0;
private terrainDisplayed: boolean;
@computed get flyInEndBearing() {
- return this.isStreetViewAnimation ?
- this.calculateBearing(
- {
- lng: this.ROUTE_COORDINATES[0][0],
- lat: this.ROUTE_COORDINATES[0][1]
- },
- {
- lng: this.ROUTE_COORDINATES[1][0],
- lat: this.ROUTE_COORDINATES[1][1]
- }
- )
+ return this.isStreetViewAnimation
+ ? this.calculateBearing(
+ {
+ lng: this.ROUTE_COORDINATES[0][0],
+ lat: this.ROUTE_COORDINATES[0][1],
+ },
+ {
+ lng: this.ROUTE_COORDINATES[1][0],
+ lat: this.ROUTE_COORDINATES[1][1],
+ }
+ )
: -20;
}
@@ -67,15 +68,14 @@ export class AnimationUtility {
const coords: mapboxgl.LngLatLike = [this.previousLngLat.lng, this.previousLngLat.lat];
// console.log('MAP REF: ', this.MAP_REF)
// console.log("current elevation: ", this.MAP_REF?.queryTerrainElevation(coords));
- let altitude = (this.MAP_REF ? (this.MAP_REF.queryTerrainElevation(coords) ?? 0) : 0);
- if (altitude === 0){
- altitude+=50;
+ let altitude = this.MAP_REF ? this.MAP_REF.queryTerrainElevation(coords) ?? 0 : 0;
+ if (altitude === 0) {
+ altitude += 50;
}
- if (this.previousAltitude){
+ if (this.previousAltitude) {
return this.lerp(altitude, this.previousAltitude, 0.02);
}
return altitude;
-
}
@computed get flyInStartBearing() {
@@ -96,16 +96,15 @@ export class AnimationUtility {
const horizontalDistance = 500;
let pitch;
- if (heightAboveGround >= 0){
- pitch = (90- Math.atan(heightAboveGround/horizontalDistance) * (180/Math.PI));
- }
- else {
+ if (heightAboveGround >= 0) {
+ pitch = 90 - Math.atan(heightAboveGround / horizontalDistance) * (180 / Math.PI);
+ } else {
pitch = 80;
}
console.log(Math.max(50, Math.min(pitch, 85)));
- if (this.previousPitch){
+ if (this.previousPitch) {
return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02);
}
return Math.max(50, Math.min(pitch, 85));
@@ -138,7 +137,7 @@ export class AnimationUtility {
const normalizedDistance = Math.min(1, (this.PATH_DISTANCE - MIN_DISTANCE) / (MAX_DISTANCE - MIN_DISTANCE));
const easedDistance = d3.easeExpOut(Math.min(normalizedDistance, 1));
- switch (this.animationSpeed){
+ switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
scalingFactor = 250;
break;
@@ -148,13 +147,13 @@ export class AnimationUtility {
case AnimationSpeed.FAST:
scalingFactor = 85;
break;
- default:
+ default:
scalingFactor = 150;
break;
}
- const duration = Math.min(MAX_DURATION, (easedDistance * MAX_DISTANCE) * (this.isStreetViewAnimation ? scalingFactor*1.12 : scalingFactor));
-
+ const duration = Math.min(MAX_DURATION, easedDistance * MAX_DISTANCE * (this.isStreetViewAnimation ? scalingFactor * 1.12 : scalingFactor));
+
return duration;
}
@@ -163,26 +162,18 @@ export class AnimationUtility {
// calculate new flyToDuration and animationDuration
this.animationSpeed = speed;
}
-
+
@action
public updateIsStreetViewAnimation(isStreetViewAnimation: boolean) {
this.isStreetViewAnimation = isStreetViewAnimation;
}
- @action
+ @action
public setPath = (path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>) => {
this.PATH = path;
- }
+ };
-
- constructor(
- firstLngLat: {lng: number, lat: number},
- routeCoordinates: Position[],
- isStreetViewAnimation: boolean,
- animationSpeed: AnimationSpeed,
- terrainDisplayed: boolean,
- mapRef: MapRef | null
- ) {
+ constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed, terrainDisplayed: boolean, mapRef: MapRef | null) {
this.FIRST_LNG_LAT = firstLngLat;
this.previousLngLat = firstLngLat;
this.isStreetViewAnimation = isStreetViewAnimation;
@@ -196,11 +187,11 @@ export class AnimationUtility {
const bearing = this.calculateBearing(
{
lng: routeCoordinates[0][0],
- lat: routeCoordinates[0][1]
+ lat: routeCoordinates[0][1],
},
{
lng: routeCoordinates[1][0],
- lat: routeCoordinates[1][1]
+ lat: routeCoordinates[1][1],
}
);
this.currentStreetViewBearing = bearing;
@@ -216,295 +207,242 @@ export class AnimationUtility {
currentAnimationPhase,
updateAnimationPhase,
updateFrameId,
- }: {
- map: MapRef,
+ }: {
+ map: MapRef;
// path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>,
// startBearing: number,
// startAltitude: number,
// pitch: number,
- currentAnimationPhase: number,
- updateAnimationPhase: (
- newAnimationPhase: number,
- ) => void,
+ currentAnimationPhase: number;
+ updateAnimationPhase: (newAnimationPhase: number) => void;
updateFrameId: (newFrameId: number) => void;
+ }) => {
+ return new Promise<void>(async resolve => {
+ let startTime: number | null = null;
+
+ const frame = async (currentTime: number) => {
+ if (!startTime) startTime = currentTime;
+ const elapsedSinceLastFrame = currentTime - startTime;
+ const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
+ const animationPhase = currentAnimationPhase + phaseIncrement;
+
+ // when the duration is complete, resolve the promise and stop iterating
+ if (animationPhase > 1) {
+ resolve();
+ return;
+ }
- }) => {
- return new Promise<void>(async (resolve) => {
- let startTime: number | null = null;
-
- const frame = async (currentTime: number) => {
- if (!startTime) startTime = currentTime;
- const elapsedSinceLastFrame = currentTime - startTime;
- const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
- const animationPhase = currentAnimationPhase + phaseIncrement;
-
- // when the duration is complete, resolve the promise and stop iterating
- if (animationPhase > 1) {
- resolve();
- return;
- }
+ // calculate the distance along the path based on the animationPhase
+ const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry.coordinates;
+
+ const lngLat = {
+ lng: alongPath[0],
+ lat: alongPath[1],
+ };
+
+ let bearing: number;
+ if (this.isStreetViewAnimation) {
+ bearing = this.lerp(this.currentStreetViewBearing, this.calculateBearing(this.previousLngLat, lngLat), 0.032);
+ this.currentStreetViewBearing = bearing;
+ // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
+ } else {
+ // slowly rotate the map at a constant rate
+ bearing = this.flyInEndBearing - animationPhase * 200.0;
+ // bearing = startBearing - animationPhase * 200.0;
+ }
+
+ runInAction(() => {
+ this.previousLngLat = lngLat;
+ });
+
+ updateAnimationPhase(animationPhase);
-
- // calculate the distance along the path based on the animationPhase
- const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry
- .coordinates;
-
- const lngLat = {
- lng: alongPath[0],
- lat: alongPath[1],
+ // compute corrected camera ground position, so that he leading edge of the path is in view
+ var correctedPosition = this.computeCameraPosition(
+ this.isStreetViewAnimation,
+ this.currentPitch,
+ bearing,
+ lngLat,
+ this.currentAnimationAltitude,
+ true // smooth
+ );
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(this.currentPitch, bearing);
+
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, this.currentAnimationAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
+
+ this.previousAltitude = this.currentAnimationAltitude;
+ this.previousPitch = this.previousPitch;
+
+ // repeat!
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
};
-
- let bearing: number;
- if (this.isStreetViewAnimation){
- bearing = this.lerp(
- this.currentStreetViewBearing,
- this.calculateBearing(this.previousLngLat, lngLat),
- 0.032
- );
- this.currentStreetViewBearing = bearing;
- // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
- } else {
- // slowly rotate the map at a constant rate
- bearing = this.flyInEndBearing - animationPhase * 200.0;
- // bearing = startBearing - animationPhase * 200.0;
- }
- runInAction(() => {
- this.previousLngLat = lngLat;
- })
-
- updateAnimationPhase(animationPhase);
-
- // compute corrected camera ground position, so that he leading edge of the path is in view
- var correctedPosition = this.computeCameraPosition(
- this.isStreetViewAnimation,
- this.currentPitch,
- bearing,
- lngLat,
- this.currentAnimationAltitude,
- true // smooth
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(this.currentPitch, bearing);
-
-
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- this.currentAnimationAltitude
- );
-
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- this.previousAltitude = this.currentAnimationAltitude;
- this.previousPitch = this.previousPitch;
-
- // repeat!
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- public flyInAndRotate = async ({
- map,
- updateFrameId
- }:
- {
- map: MapRef,
- updateFrameId: (newFrameId: number) => void
- }
- ) => {
- return new Promise<{bearing: number, altitude: number}>(async (resolve) => {
- let start: number | null;
-
- var currentAltitude;
- var currentBearing;
- var currentPitch;
-
- // the animation frame will run as many times as necessary until the duration has been reached
- const frame = async (time: number) => {
- if (!start) {
- start = time;
- }
-
- // otherwise, use the current time to determine how far along in the duration we are
- let animationPhase = (time - start) / this.flyToDuration;
-
- // because the phase calculation is imprecise, the final zoom can vary
- // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
- if (animationPhase > 1) {
- animationPhase = 1;
- }
-
- currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase)
- // rotate the camera between startBearing and endBearing
- currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase)
-
- currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase)
-
- // compute corrected camera ground position, so the start of the path is always in view
- var correctedPosition = this.computeCameraPosition(
- false,
- currentPitch,
- currentBearing,
- this.FIRST_LNG_LAT,
- currentAltitude
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(currentPitch, currentBearing);
-
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- currentAltitude
- );
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
- if (animationPhase === 1) {
- resolve({
- bearing: currentBearing,
- altitude: currentAltitude,
- });
-
- // return so there are no further iterations of this frame
- return;
- }
-
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
-
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ public flyInAndRotate = async ({ map, updateFrameId }: { map: MapRef; updateFrameId: (newFrameId: number) => void }) => {
+ return new Promise<{ bearing: number; altitude: number }>(async resolve => {
+ let start: number | null;
+
+ var currentAltitude;
+ var currentBearing;
+ var currentPitch;
+
+ // the animation frame will run as many times as necessary until the duration has been reached
+ const frame = async (time: number) => {
+ if (!start) {
+ start = time;
+ }
+
+ // otherwise, use the current time to determine how far along in the duration we are
+ let animationPhase = (time - start) / this.flyToDuration;
+
+ // because the phase calculation is imprecise, the final zoom can vary
+ // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
+ if (animationPhase > 1) {
+ animationPhase = 1;
+ }
+
+ currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase);
+ // rotate the camera between startBearing and endBearing
+ currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase);
+
+ currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase);
+
+ // compute corrected camera ground position, so the start of the path is always in view
+ var correctedPosition = this.computeCameraPosition(false, currentPitch, currentBearing, this.FIRST_LNG_LAT, currentAltitude);
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(currentPitch, currentBearing);
+
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, currentAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
+
+ // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
+ if (animationPhase === 1) {
+ resolve({
+ bearing: currentBearing,
+ altitude: currentAltitude,
+ });
+
+ // return so there are no further iterations of this frame
+ return;
+ }
+
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
+ };
+
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- previousCameraPosition: {lng: number, lat: number} | null = null;
+ previousCameraPosition: { lng: number; lat: number } | null = null;
- lerp = (start: number, end: number, amt: number) => {
+ lerp = (start: number, end: number, amt: number) => {
return (1 - amt) * start + amt * end;
- }
-
- computeCameraPosition = (
- isStreetViewAnimation: boolean,
- pitch: number,
- bearing: number,
- targetPosition: {lng: number, lat: number},
- altitude: number,
- smooth = false
- ) => {
+ };
+
+ computeCameraPosition = (isStreetViewAnimation: boolean, pitch: number, bearing: number, targetPosition: { lng: number; lat: number }, altitude: number, smooth = false) => {
const bearingInRadian = (bearing * Math.PI) / 180;
- const pitchInRadian = ((90 - pitch)* Math.PI) / 180;
+ const pitchInRadian = ((90 - pitch) * Math.PI) / 180;
let correctedLng = targetPosition.lng;
let correctedLat = targetPosition.lat;
if (!isStreetViewAnimation) {
- const lngDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.sin(-bearingInRadian)) /
- 70000; // ~70km/degree longitude
- const latDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.cos(-bearingInRadian)) /
- 110000 // 110km/degree latitude
+ const lngDiff = ((altitude / Math.tan(pitchInRadian)) * Math.sin(-bearingInRadian)) / 70000; // ~70km/degree longitude
+ const latDiff = ((altitude / Math.tan(pitchInRadian)) * Math.cos(-bearingInRadian)) / 110000; // 110km/degree latitude
correctedLng = targetPosition.lng + lngDiff;
correctedLat = targetPosition.lat - latDiff;
-
}
-
+
const newCameraPosition = {
- lng: correctedLng,
- lat: correctedLat
+ lng: correctedLng,
+ lat: correctedLat,
};
-
+
if (smooth) {
- if (this.previousCameraPosition) {
- newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
- newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
- }
+ if (this.previousCameraPosition) {
+ newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
+ newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
+ }
}
-
- this.previousCameraPosition = newCameraPosition
-
+
+ this.previousCameraPosition = newCameraPosition;
+
return newCameraPosition;
- };
-
- public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties>=> {
+ };
+
+ public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties> => {
const coords = {
- latitude: center[1],
- longitude: center[0],
+ latitude: center[1],
+ longitude: center[0],
};
const km = radiusInKm;
const ret = [];
- const distanceX = km / (111.320 * Math.cos((coords.latitude * Math.PI) / 180));
+ const distanceX = km / (111.32 * Math.cos((coords.latitude * Math.PI) / 180));
const distanceY = km / 110.574;
let theta;
let x;
let y;
for (let i = 0; i < points; i += 1) {
- theta = (i / points) * (2 * Math.PI);
- x = distanceX * Math.cos(theta);
- y = distanceY * Math.sin(theta);
- ret.push([coords.longitude + x, coords.latitude + y]);
+ theta = (i / points) * (2 * Math.PI);
+ x = distanceX * Math.cos(theta);
+ y = distanceY * Math.sin(theta);
+ ret.push([coords.longitude + x, coords.latitude + y]);
}
ret.push(ret[0]);
return {
- type: 'Feature',
- geometry: {
- type: 'Polygon',
- coordinates: [ret],
- },
- properties: {}
+ type: 'Feature',
+ geometry: {
+ type: 'Polygon',
+ coordinates: [ret],
+ },
+ properties: {},
};
- }
+ };
- private calculateBearing(
- from: { lng: number; lat: number },
- to: { lng: number; lat: number }
- ): number {
+ private calculateBearing(from: { lng: number; lat: number }, to: { lng: number; lat: number }): number {
const lon1 = from.lng;
const lat1 = from.lat;
const lon2 = to.lng;
const lat2 = to.lat;
-
+
const lon1Rad = (lon1 * Math.PI) / 180;
const lon2Rad = (lon2 * Math.PI) / 180;
const lat1Rad = (lat1 * Math.PI) / 180;
const lat2Rad = (lat2 * Math.PI) / 180;
-
+
const y = Math.sin(lon2Rad - lon1Rad) * Math.cos(lat2Rad);
- const x =
- Math.cos(lat1Rad) * Math.sin(lat2Rad) -
- Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
-
- let bearing = Math.atan2(y,x);
-
+ const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
+
+ let bearing = Math.atan2(y, x);
+
// Convert bearing from radians to degrees
bearing = (bearing * 180) / Math.PI;
-
+
// Ensure the bearing is within [0, 360)
if (bearing < 0) {
- bearing += 360;
+ bearing += 360;
}
-
- return bearing;
- }
-
-} \ No newline at end of file
+ return bearing;
+ }
+}
diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
index bf4028f01..f9607becf 100644
--- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
@@ -1,15 +1,15 @@
-import React = require('react');
-import { observer } from "mobx-react";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { IReactionDisposer, ObservableMap, reaction } from "mobx";
-import { Doc, Opt } from "../../../../fields/Doc";
-import { returnFalse, unimplementedFunction } from "../../../../Utils";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { IconButton } from "browndash-components";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { SettingsManager } from "../../../util/SettingsManager";
-import { IconLookup, faAdd, faCalendarDays, faRoute } from "@fortawesome/free-solid-svg-icons";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { returnFalse, unimplementedFunction } from '../../../../Utils';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { IconButton } from 'browndash-components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons';
@observer
export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -32,9 +32,9 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private title: string | undefined = undefined;
- public setPinDoc(pinDoc: Doc){
- this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
- console.log("Title: ", this.title)
+ public setPinDoc(pinDoc: Doc) {
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
+ console.log('Title: ', this.title);
}
public get Active() {
@@ -54,7 +54,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
sel => DirectionsAnchorMenu.Instance.fadeOut(true)
);
}
@@ -95,43 +95,28 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
render() {
const buttons = (
- <div className='directions-menu-buttons' style={{display: 'flex'}}>
+ <div className="directions-menu-buttons" style={{ display: 'flex' }}>
<IconButton
tooltip="Add route" //
onPointerDown={this.Delete}
icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
color={SettingsManager.userColor}
/>
-
-
- <IconButton
- tooltip='Animate route'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+
+ <IconButton tooltip="Animate route" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
</div>
- )
+ );
return this.getElement(
- <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content' , width: '100%', display: 'flex', flexDirection: 'column' }}>
+ <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content', width: '100%', display: 'flex', flexDirection: 'column' }}>
<div>{this.title}</div>
- <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
- <input
- placeholder="Origin"
- />
- <input
- placeholder="Destination"
- />
+ <div className="direction-inputs" style={{ display: 'flex', flexDirection: 'column' }}>
+ <input placeholder="Origin" />
+ <input placeholder="Destination" />
</div>
{buttons}
</div>
- )
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f4e24d9c1..b1fb3368c 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, action, observable, reaction, runInAction } from 'mobx';
+import * as React from 'react';
+import { IReactionDisposer, ObservableMap, action, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
@@ -11,29 +11,14 @@ import { Button, IconButton } from 'browndash-components';
import { SettingsManager } from '../../../util/SettingsManager';
import './MapAnchorMenu.scss';
import { NumCast, StrCast } from '../../../../fields/Types';
-import {
- IconLookup,
- faDiamondTurnRight,
- faCalendarDays,
- faEdit,
- faAdd,
- faRoute,
- faArrowLeft,
- faLocationDot,
- faArrowDown,
- faCar,
- faBicycle,
- faPersonWalking,
- faUpload,
- faArrowsRotate,
- } from '@fortawesome/free-solid-svg-icons';
+import { IconLookup, faDiamondTurnRight, faCalendarDays, faEdit, faAdd, faRoute, faArrowLeft, faLocationDot, faArrowDown, faCar, faBicycle, faPersonWalking, faUpload, faArrowsRotate } from '@fortawesome/free-solid-svg-icons';
import { DirectionsAnchorMenu } from './DirectionsAnchorMenu';
import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { MapBox } from './MapBox';
import { List } from '../../../../fields/List';
import { MarkerIcons } from './MarkerIcons';
-import { CirclePicker, ColorState } from 'react-color';
+import { CirclePicker, ColorResult } from 'react-color';
import { Position } from 'geojson';
type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
@@ -75,8 +60,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
public setMenuType = (menuType: MapAnchorMenuType) => {
this.menuType = menuType;
- }
-
+ };
private allMapPinDocs: Doc[] = [];
@@ -86,31 +70,30 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private title: string | undefined = undefined;
-
- public setPinDoc(pinDoc: Doc){
+ public setPinDoc(pinDoc: Doc) {
this.pinDoc = pinDoc;
- this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
}
- public setRouteDoc(routeDoc: Doc){
+ public setRouteDoc(routeDoc: Doc) {
this.routeDoc = routeDoc;
- this.title = StrCast(routeDoc.title ?? 'Map route')
+ this.title = StrCast(routeDoc.title ?? 'Map route');
}
public setAllMapboxPins(pinDocs: Doc[]) {
this.allMapPinDocs = pinDocs;
pinDocs.forEach((p, idx) => {
console.log(`Pin ${idx}: ${p.title}`);
- })
- }
+ });
+ }
public get Active() {
return this._left > 0;
}
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
-
+ makeObservable(this);
MapAnchorMenu.Instance = this;
MapAnchorMenu.Instance._canFade = false;
}
@@ -125,7 +108,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
sel => MapAnchorMenu.Instance.fadeOut(true)
);
}
@@ -164,55 +147,52 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// return this.top
// }
-
-
@action
DirectionsClick = () => {
this.menuType = 'routeCreation';
- }
+ };
@action
CustomizeClick = () => {
this.currentRouteInfoMap = undefined;
this.menuType = 'customize';
- }
+ };
@action
BackClick = () => {
this.currentRouteInfoMap = undefined;
this.menuType = 'standard';
- }
+ };
@action
TriggerFileInputClick = () => {
if (this._fileInputRef) {
this._fileInputRef.current?.click(); // Trigger the file input click event
- }
- }
+ }
+ };
- @action
- onMarkerColorChange = (color: ColorState) => {
- if (this.pinDoc){
+ @action
+ onMarkerColorChange = (color: ColorResult) => {
+ if (this.pinDoc) {
this.pinDoc.markerColor = color.hex;
}
- }
+ };
revertToOriginalMarker = () => {
if (this.pinDoc) {
- this.pinDoc.markerType = "MAP_PIN";
- this.pinDoc.markerColor = "#ff5722";
+ this.pinDoc.markerType = 'MAP_PIN';
+ this.pinDoc.markerColor = '#ff5722';
}
- }
+ };
onMarkerIconChange = (iconKey: string) => {
if (this.pinDoc) {
this.pinDoc.markerType = iconKey;
}
- }
-
+ };
@observable
- destinationFeatures: any[] = []
+ destinationFeatures: any[] = [];
@observable
destinationSelected: boolean = false;
@@ -220,7 +200,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable
selectedDestinationFeature: any = undefined;
- @observable
+ @observable
createPinForDestination: boolean = true;
@observable
@@ -231,98 +211,85 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
handleTransportationTypeChange = (newType: TransportationType) => {
- if (newType !== this.selectedTransportationType){
+ if (newType !== this.selectedTransportationType) {
this.selectedTransportationType = newType;
this.DisplayRoute(this.currentRouteInfoMap, newType);
}
-
- }
+ };
@action
handleSelectedDestinationFeature = (destinationFeature: any) => {
this.selectedDestinationFeature = destinationFeature;
- }
+ };
@action
toggleCreatePinForDestinationCheckbox = () => {
this.createPinForDestination = !this.createPinForDestination;
- }
+ };
@action
handleDestinationSearchChange = async (searchText: string) => {
if (this.selectedDestinationFeature !== undefined) this.selectedDestinationFeature = undefined;
const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
- if (features){
+ if (features) {
runInAction(() => {
this.destinationFeatures = features;
-
- })
+ });
}
- }
+ };
getRoutes = async (destinationFeature: any) => {
const currentPinLong: number = NumCast(this.pinDoc?.longitude);
const currentPinLat: number = NumCast(this.pinDoc?.latitude);
- if (currentPinLong && currentPinLat && destinationFeature.center){
+ if (currentPinLong && currentPinLat && destinationFeature.center) {
const routeInfoMap = await MapboxApiUtility.getDirections([currentPinLong, currentPinLat], destinationFeature.center);
if (routeInfoMap) {
runInAction(() => {
this.currentRouteInfoMap = routeInfoMap;
- })
+ });
this.DisplayRoute(routeInfoMap, 'driving');
}
}
- // get route menu, set it equal to here
- // create a temporary route
+ // get route menu, set it equal to here
+ // create a temporary route
// create pin if createPinForDestination was clicked
- }
+ };
HandleAddRouteClick = () => {
- if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature){
+ if (this.currentRouteInfoMap && this.selectedTransportationType && this.selectedDestinationFeature) {
const coordinates = this.currentRouteInfoMap[this.selectedTransportationType].coordinates;
console.log(coordinates);
console.log(this.selectedDestinationFeature);
- this.AddNewRouteToMap(coordinates, this.title ?? "", this.selectedDestinationFeature, this.createPinForDestination);
+ this.AddNewRouteToMap(coordinates, this.title ?? '', this.selectedDestinationFeature, this.createPinForDestination);
this.HideRoute();
}
- }
+ };
getMarkerIcon = (): JSX.Element | undefined => {
- if (this.pinDoc){
+ if (this.pinDoc) {
const markerType = StrCast(this.pinDoc.markerType);
const markerColor = StrCast(this.pinDoc.markerColor);
return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor);
}
return undefined;
- }
-
+ };
render() {
const buttons = (
- <div className='menu-buttons' style={{display: 'flex'}}>
- {this.menuType === 'standard' &&
+ <div className="menu-buttons" style={{ display: 'flex' }}>
+ {this.menuType === 'standard' && (
<>
<IconButton
- tooltip="Delete Pin" //
- onPointerDown={this.Delete}
- icon={<FontAwesomeIcon icon="trash-alt" />}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Get directions'
- onPointerDown={this.DirectionsClick} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup}/>}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
+ tooltip="Delete Pin" //
+ onPointerDown={this.Delete}
+ icon={<FontAwesomeIcon icon="trash-alt" />}
color={SettingsManager.userColor}
/>
+ <IconButton tooltip="Get directions" onPointerDown={this.DirectionsClick} /**TODO: fix */ icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
<div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
@@ -331,12 +298,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</div>
- <IconButton
- tooltip="Customize pin"
- onPointerDown={this.CustomizeClick}
- icon={<FontAwesomeIcon icon={faEdit as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+ <IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />
<IconButton
tooltip="Center on pin" //
onPointerDown={this.Center}
@@ -344,8 +306,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
- {this.menuType === 'routeCreation' &&
+ )}
+ {this.menuType === 'routeCreation' && (
<>
<IconButton
tooltip="Go back" //
@@ -360,8 +322,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
- {this.menuType === 'route' &&
+ )}
+ {this.menuType === 'route' && (
<>
<IconButton
tooltip="Delete Route" //
@@ -369,12 +331,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
icon={<FontAwesomeIcon icon="trash-alt" />}
color={SettingsManager.userColor}
/>
- <IconButton
- tooltip='Animate route'
- onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+ <IconButton tooltip="Animate route" onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} /**TODO: fix */ icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />
<div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
@@ -383,17 +340,10 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</div>
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
- color={SettingsManager.userColor}
- />
-
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
</>
-
- }
- {this.menuType === 'customize' &&
+ )}
+ {this.menuType === 'customize' && (
<>
<IconButton
tooltip="Go back" //
@@ -408,9 +358,8 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
</>
- }
-
-
+ )}
+
{/* {this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
@@ -432,68 +381,37 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// )
return this.getElement(
- <div
- ref={MapAnchorMenu.top}
- className='map-anchor-menu-container'>
- {this.menuType === 'standard' &&
- <div>{this.title}</div>
- }
- {this.menuType === 'routeCreation' &&
- <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
- <TextField
- fullWidth
- disabled
- value={this.title}
- />
- <FontAwesomeIcon icon={faArrowDown as IconLookup} size='xs'/>
+ <div ref={MapAnchorMenu.top} className="map-anchor-menu-container">
+ {this.menuType === 'standard' && <div>{this.title}</div>}
+ {this.menuType === 'routeCreation' && (
+ <div className="direction-inputs" style={{ display: 'flex', flexDirection: 'column' }}>
+ <TextField fullWidth disabled value={this.title} />
+ <FontAwesomeIcon icon={faArrowDown as IconLookup} size="xs" />
<Autocomplete
fullWidth
id="route-destination-searcher"
-
- onInputChange={(e, searchText) => this.handleDestinationSearchChange(searchText)}
- onChange={(e, feature, reason) => {
- if (reason === 'clear'){
+ onInputChange={(e: any, searchText: any) => this.handleDestinationSearchChange(searchText)}
+ onChange={(e: any, feature: any, reason: any) => {
+ if (reason === 'clear') {
this.handleSelectedDestinationFeature(undefined);
- } else if (reason === 'selectOption'){
+ } else if (reason === 'selectOption') {
this.handleSelectedDestinationFeature(feature);
}
}}
- options={this.destinationFeatures
- .filter(feature => feature.place_name)
- .map(feature => feature)}
- getOptionLabel={(feature) => feature.place_name}
- renderInput={(params) => (
- <TextField
- {...params}
- placeholder='Enter a destination'
- />
- )}
+ options={this.destinationFeatures.filter(feature => feature.place_name).map(feature => feature)}
+ getOptionLabel={(feature: any) => feature.place_name}
+ renderInput={(params: any) => <TextField {...params} placeholder="Enter a destination" />}
/>
- {this.selectedDestinationFeature &&
+ {this.selectedDestinationFeature && (
<>
- {!this.allMapPinDocs.some(pinDoc => pinDoc.title === this.selectedDestinationFeature.place_name) &&
- <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '5px'}}>
- <FormControlLabel
- label='Create pin for destination?'
- control={
- <Checkbox
- color='success'
- checked={this.createPinForDestination}
- onChange={this.toggleCreatePinForDestinationCheckbox}
- />
- }
- />
- </div>
- }
+ {!this.allMapPinDocs.some(pinDoc => pinDoc.title === this.selectedDestinationFeature.place_name) && (
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '5px' }}>
+ <FormControlLabel label="Create pin for destination?" control={<Checkbox color="success" checked={this.createPinForDestination} onChange={this.toggleCreatePinForDestinationCheckbox} />} />
+ </div>
+ )}
</>
-
-
- }
- <button
- id='get-routes-button'
- disabled={this.selectedDestinationFeature ? false : true}
- onClick={() => this.getRoutes(this.selectedDestinationFeature)}
- >
+ )}
+ <button id="get-routes-button" disabled={this.selectedDestinationFeature ? false : true} onClick={() => this.getRoutes(this.selectedDestinationFeature)}>
Get routes
</button>
@@ -501,74 +419,58 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
placeholder="Origin"
/> */}
</div>
- }
- {this.currentRouteInfoMap &&
- <div className='current-route-info-container'>
- <div className='transportation-icons-container'>
+ )}
+ {this.currentRouteInfoMap && (
+ <div className="current-route-info-container">
+ <div className="transportation-icons-container">
<IconButton
- tooltip="Driving route"
+ tooltip="Driving route"
onPointerDown={() => this.handleTransportationTypeChange('driving')}
- icon={<FontAwesomeIcon icon={faCar as IconLookup}/>}
- color={this.selectedTransportationType === 'driving' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faCar as IconLookup} />}
+ color={this.selectedTransportationType === 'driving' ? 'lightblue' : 'grey'}
/>
<IconButton
- tooltip="Cycling route"
+ tooltip="Cycling route"
onPointerDown={() => this.handleTransportationTypeChange('cycling')}
- icon={<FontAwesomeIcon icon={faBicycle as IconLookup}/>}
- color={this.selectedTransportationType === 'cycling' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faBicycle as IconLookup} />}
+ color={this.selectedTransportationType === 'cycling' ? 'lightblue' : 'grey'}
/>
<IconButton
- tooltip="Walking route"
+ tooltip="Walking route"
onPointerDown={() => this.handleTransportationTypeChange('walking')}
- icon={<FontAwesomeIcon icon={faPersonWalking as IconLookup}/>}
- color={this.selectedTransportationType === 'walking' ? 'lightblue': 'grey'}
+ icon={<FontAwesomeIcon icon={faPersonWalking as IconLookup} />}
+ color={this.selectedTransportationType === 'walking' ? 'lightblue' : 'grey'}
/>
</div>
- <div className='selected-route-details-container'>
+ <div className="selected-route-details-container">
<div>Duration: {this.currentRouteInfoMap[this.selectedTransportationType].duration}</div>
<div>Distance: {this.currentRouteInfoMap[this.selectedTransportationType].distance}</div>
</div>
</div>
-
-
- }
- {this.menuType === 'customize' &&
- <div className='customized-marker-container'>
- <div className='current-marker-container'>
- <div>Current Marker: </div>
- <div>
- {this.getMarkerIcon()}
+ )}
+ {this.menuType === 'customize' && (
+ <div className="customized-marker-container">
+ <div className="current-marker-container">
+ <div>Current Marker: </div>
+ <div>{this.getMarkerIcon()}</div>
</div>
+ <div className="color-picker-container" style={{ marginBottom: '10px' }}>
+ <CirclePicker circleSize={15} circleSpacing={7} width="100%" onChange={color => this.onMarkerColorChange(color)} />
+ </div>
+ <div className="all-markers-container">
+ {Object.keys(MarkerIcons.FAMarkerIconsMap).map(iconKey => (
+ <div key={iconKey} className="marker-icon">
+ <IconButton onPointerDown={() => this.onMarkerIconChange(iconKey)} icon={MarkerIcons.getFontAwesomeIcon(iconKey, '1x', 'white')} />
+ </div>
+ ))}
+ </div>
+ <div style={{ width: '100%', height: '3px', color: 'white' }}></div>
</div>
- <div className='color-picker-container' style={{marginBottom: '10px'}}>
- <CirclePicker
- circleSize={15}
- circleSpacing={7}
- width='100%'
- onChange={(color) => this.onMarkerColorChange(color)}
- />
- </div>
- <div className='all-markers-container'>
- {Object.keys(MarkerIcons.FAMarkerIconsMap).map((iconKey) => (
- <div key={iconKey} className='marker-icon'>
- <IconButton
- onPointerDown={() => this.onMarkerIconChange(iconKey)}
- icon={MarkerIcons.getFontAwesomeIcon(iconKey, '1x', 'white')}
- />
- </div>
- ))}
- </div>
- <div style={{width: '100%', height:'3px', color: 'white'}}></div>
- </div>
- }
- {this.menuType === 'route' && this.routeDoc &&
- <div>
- {StrCast(this.routeDoc.title)}
- </div>
-
- }
+ )}
+ {this.menuType === 'route' && this.routeDoc && <div>{StrCast(this.routeDoc.title)}</div>}
{buttons}
- </div>
- , true);
+ </div>,
+ true
+ );
}
}
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index e25261729..434e02b27 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.scss';
+@import '../../global/globalCssVariables.module.scss';
.mapBox {
width: 100%;
height: 100%;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 6bad7d724..fdd8285d5 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -1,597 +1,597 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { UndoManager } from '../../../util/UndoManager';
-import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
-import { AnchorMenu } from '../../pdf/AnchorMenu';
-import { Annotation } from '../../pdf/Annotation';
-import { SidebarAnnos } from '../../SidebarAnnos';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
-import './MapBox2.scss';
-import { MapBoxInfoWindow } from './MapBoxInfoWindow';
-
-/**
- * MapBox2 architecture:
- * Main component: MapBox2.tsx
- * Supporting Components: SidebarAnnos, CollectionStackingView
- *
- * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
- * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
- * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
- * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
- * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
- * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
- */
-
-// const _global = (window /* browser */ || global /* node */) as any;
-
-const mapContainerStyle = {
- height: '100%',
-};
-
-const defaultCenter = {
- lat: 42.360081,
- lng: -71.058884,
-};
-
-const mapOptions = {
- fullscreenControl: false,
-};
-
-const apiKey = process.env.GOOGLE_MAPS;
-
-const script = document.createElement('script');
-script.defer = true;
-script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
-document.head.appendChild(script);
-
-/**
- * Consider integrating later: allows for drawing, circling, making shapes on map
- */
-// const drawingManager = new window.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,
-// ],
-// },
-// });
-
-// options for searchbox in Google Maps Places Autocomplete API
-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 MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _disposers: { [name: string]: IReactionDisposer } = {};
- private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- @observable private _overlayAnnoInfo: Opt<Doc>;
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(MapBox2, fieldKey);
- }
- public get SidebarKey() {
- return this.fieldKey + '_sidebar';
- }
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- @computed get inlineTextAnnotations() {
- return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
- }
-
- @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
- @observable private selectedPlace: Doc | undefined;
- @observable private markerMap: { [id: string]: google.maps.Marker } = {};
- @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
- @observable private inputRef = React.createRef<HTMLInputElement>();
- @observable private searchMarkers: google.maps.Marker[] = [];
- @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
- @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @computed get allSidebarDocs() {
- return DocListCast(this.dataDoc[this.SidebarKey]);
- }
- @computed get allMapMarkers() {
- return DocListCast(this.dataDoc[this.annotationKey]);
- }
- @observable private toggleAddMarker = false;
-
- @observable _showSidebar = false;
- @computed get SidebarShown() {
- return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
- }
-
- static _canAnnotate = true;
- static _hadSelection: boolean = false;
- private _sidebarRef = React.createRef<SidebarAnnos>();
- private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
- componentDidMount() {
- this.props.setContentView?.(this);
- }
-
- @action
- private setSearchBox = (searchBox: any) => {
- this.searchBox = searchBox;
- };
-
- // iterate allMarkers to size, center, and zoom map to contain all markers
- private fitBounds = (map: google.maps.Map) => {
- const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
- const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
- !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
- };
-
- /**
- * Custom control for add marker button
- * @param controlDiv
- * @param map
- */
- private CenterControl = () => {
- const controlDiv = document.createElement('div');
- controlDiv.className = 'MapBox2-addMarker';
- // Set CSS for the control border.
- const controlUI = document.createElement('div');
- controlUI.style.backgroundColor = '#fff';
- controlUI.style.borderRadius = '3px';
- controlUI.style.cursor = 'pointer';
- controlUI.style.marginTop = '10px';
- controlUI.style.borderRadius = '4px';
- controlUI.style.marginBottom = '22px';
- controlUI.style.textAlign = 'center';
- controlUI.style.position = 'absolute';
- controlUI.style.width = '32px';
- controlUI.style.height = '32px';
- controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
-
- const plIcon = document.createElement('img');
- plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
- plIcon.style.color = 'rgb(25,25,25)';
- plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- plIcon.style.fontSize = '16px';
- plIcon.style.lineHeight = '32px';
- plIcon.style.left = '18';
- plIcon.style.top = '15';
- plIcon.style.position = 'absolute';
- plIcon.width = 14;
- plIcon.height = 14;
- plIcon.innerHTML = 'Add';
- controlUI.appendChild(plIcon);
-
- // Set CSS for the control interior.
- const markerIcon = document.createElement('img');
- markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
- markerIcon.style.color = 'rgb(25,25,25)';
- markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- markerIcon.style.fontSize = '16px';
- markerIcon.style.lineHeight = '32px';
- markerIcon.style.left = '-2';
- markerIcon.style.top = '1';
- markerIcon.width = 30;
- markerIcon.height = 30;
- markerIcon.style.position = 'absolute';
- markerIcon.innerHTML = 'Add';
- controlUI.appendChild(markerIcon);
-
- // Setup the click event listeners
- controlUI.addEventListener('click', () => {
- if (this.toggleAddMarker === true) {
- this.toggleAddMarker = false;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#fff';
- markerIcon.style.color = 'rgb(25,25,25)';
- } else {
- this.toggleAddMarker = true;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#4476f7';
- markerIcon.style.color = 'rgb(255,255,255)';
- }
- });
- controlDiv.appendChild(controlUI);
- return controlDiv;
- };
-
- /**
- * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
- * @param position - the LatLng position where the marker is placed
- * @param map
- */
- @action
- private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
- const marker = new google.maps.Marker({
- position: position,
- map: map,
- });
- map.panTo(position);
- const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
- this.addDocument(mapMarker, this.annotationKey);
- };
-
- _loadPending = true;
- /**
- * store a reference to google map instance
- * setup the drawing manager on the top right corner of map
- * fit map bounds to contain all markers
- * @param map
- */
- @action
- private loadHandler = (map: google.maps.Map) => {
- this._map = map;
- this._loadPending = true;
- const centerControlDiv = this.CenterControl();
- map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
- //drawingManager.setMap(map);
- // if (navigator.geolocation) {
- // navigator.geolocation.getCurrentPosition(
- // (position: Position) => {
- // const pos = {
- // lat: position.coords.latitude,
- // lng: position.coords.longitude,
- // };
- // this._map.setCenter(pos);
- // }
- // );
- // } else {
- // alert("Your geolocation is not supported by browser.")
- // };
- map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
- map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
- setTimeout(() => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- }, 250);
- // listener to addmarker event
- this._map.addListener('click', (e: MouseEvent) => {
- if (this.toggleAddMarker === true) {
- this.placeMarker((e as any).latLng, map);
- }
- });
- };
-
- @action
- centered = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.mapLat = this._map.getCenter()?.lat();
- this.dataDoc.mapLng = this._map.getCenter()?.lng();
- };
-
- @action
- zoomChanged = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.map_zoom = this._map.getZoom();
- };
-
- /**
- * Load and render all map markers
- * @param marker
- * @param place
- */
- @action
- private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
- place[Id] ? (this.markerMap[place[Id]] = marker) : null;
- };
-
- /**
- * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
- * @param e
- * @param place
- */
- @action
- private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
- // set which place was clicked
- this.selectedPlace = place;
- place.infoWindowOpen = true;
- };
-
- /**
- * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- console.log('print all sidebar Docs');
- if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(doc => {
- if (doc.lat !== undefined && doc.lng !== undefined) {
- const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
- if (existingMarker) {
- Doc.AddDocToList(existingMarker, 'data', doc);
- } else {
- const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
- this.addDocument(marker, this.annotationKey);
- }
- }
- }); //add to annotation list
-
- return this.addDocument(doc, sidebarKey); // add to sidebar list
- };
-
- /**
- * Removing documents from the sidebar
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- return this.removeDocument(doc, sidebarKey);
- };
-
- /**
- * Toggle sidebar onclick the tiny comment button on the top right corner
- * @param e
- */
- sidebarBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(
- this,
- e,
- (e, down, delta) =>
- runInAction(() => {
- const localDelta = this.props
- .ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
- const fullWidth = NumCast(this.layoutDoc._width);
- const mapWidth = fullWidth - this.sidebarWidth();
- if (this.sidebarWidth() + localDelta[0] > 0) {
- this._showSidebar = true;
- this.layoutDoc._width = fullWidth + localDelta[0];
- this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
- } else {
- this._showSidebar = false;
- this.layoutDoc._width = mapWidth;
- this.layoutDoc._layout_sidebarWidthPercent = '0%';
- }
- return false;
- }),
- emptyFunction,
- () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
- );
- };
-
- sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
- @computed get layout_sidebarWidthPercent() {
- return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
- }
- @computed get sidebarColor() {
- return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
- }
-
- /**
- * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
- * add a customized temporary marker on the map
- */
- @action
- private handlePlaceChanged = () => {
- const place = this.searchBox.getPlace();
-
- if (!place.geometry || !place.geometry.location) {
- // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
- window.alert("No details available for input: '" + place.name + "'");
- return;
- }
-
- // zoom in on the location of the search result
- if (place.geometry.viewport) {
- this._map.fitBounds(place.geometry.viewport);
- } else {
- this._map.setCenter(place.geometry.location);
- this._map.setZoom(17);
- }
-
- // customize icon => customized icon for the nature of the location selected
- const icon = {
- url: place.icon as string,
- size: new google.maps.Size(71, 71),
- origin: new google.maps.Point(0, 0),
- anchor: new google.maps.Point(17, 34),
- scaledSize: new google.maps.Size(25, 25),
- };
-
- // put temporary cutomized marker on searched location
- this.searchMarkers.forEach(marker => {
- marker.setMap(null);
- });
- this.searchMarkers = [];
- this.searchMarkers.push(
- new window.google.maps.Marker({
- map: this._map,
- icon,
- title: place.name,
- position: place.geometry.location,
- })
- );
- };
-
- /**
- * Handles toggle of sidebar on click the little comment button
- */
- @computed get sidebarHandle() {
- return (
- <div
- className="MapBox2-overlayButton-sidebar"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={this.sidebarBtnDown}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
- </div>
- );
- }
-
- // TODO: Adding highlight box layer to Maps
- @action
- toggleSidebar = () => {
- //1.2 * w * ? = .2 * w .2/1.2
- const prevWidth = this.sidebarWidth();
- this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
- this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
- };
-
- sidebarDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
- };
- sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const bounds = this._ref.current!.getBoundingClientRect();
- this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
- this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
- e.preventDefault();
- return false;
- };
-
- setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-
- addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
- return this.addDocument(doc, annotationKey);
- };
-
- pointerEvents = () => {
- return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
- };
- @computed get annotationLayer() {
- return (
- <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
- {this.inlineTextAnnotations
- .sort((a, b) => NumCast(a.y) - NumCast(b.y))
- .map(anno => (
- <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
- ))}
- </div>
- );
- }
-
- getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;
-
- /**
- * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
- * @returns
- */
- private renderMarkers = () => {
- return this.allMapMarkers.map(place => (
- <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
- ));
- };
-
- // TODO: auto center on select a document in the sidebar
- private handleMapCenter = (map: google.maps.Map) => {
- // console.log("print the selected views in selectionManager:")
- // if (SelectionManager.Views().lastElement()) {
- // console.log(SelectionManager.Views().lastElement());
- // }
- };
-
- 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;
-
- get MicrosoftMaps() {
- return (window as any).Microsoft.Maps;
- }
- render() {
- const renderAnnotations = (childFilters?: () => string[]) => null;
- return (
- <div className="MapBox2" ref={this._ref}>
- <div
- className="MapBox2-wrapper"
- onWheel={e => e.stopPropagation()}
- onPointerDown={async e => {
- e.button === 0 && !e.ctrlKey && e.stopPropagation();
- }}
- style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
- <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
- {renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
- {this.annotationLayer}
-
- <div>
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...this.props}
- setContentView={emptyFunction}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
- </div>
- </div>
- {/* </LoadScript > */}
- <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <SidebarAnnos
- ref={this._sidebarRef}
- {...this.props}
- fieldKey={this.fieldKey}
- rootDoc={this.rootDoc}
- layoutDoc={this.layoutDoc}
- dataDoc={this.dataDoc}
- usePanelWidth={true}
- showSidebar={this.SidebarShown}
- nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- PanelWidth={this.sidebarWidth}
- sidebarAddDocument={this.sidebarAddDocument}
- moveDocument={this.moveDocument}
- removeDocument={this.sidebarRemoveDocument}
- />
- </div>
- {this.sidebarHandle}
- </div>
- );
- }
-}
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+// import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { NumCast, StrCast } from '../../../../fields/Types';
+// import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { DragManager } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { UndoManager } from '../../../util/UndoManager';
+// import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+// import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { Colors } from '../../global/globalEnums';
+// import { AnchorMenu } from '../../pdf/AnchorMenu';
+// import { Annotation } from '../../pdf/Annotation';
+// import { SidebarAnnos } from '../../SidebarAnnos';
+// import { FieldView, FieldViewProps } from '../FieldView';
+// import { PinProps } from '../trails';
+// import './MapBox2.scss';
+// import { MapBoxInfoWindow } from './MapBoxInfoWindow';
+
+// /**
+// * MapBox2 architecture:
+// * Main component: MapBox2.tsx
+// * Supporting Components: SidebarAnnos, CollectionStackingView
+// *
+// * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+// * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+// * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
+// * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
+// * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
+// * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
+// */
+
+// // const _global = (window /* browser */ || global /* node */) as any;
+
+// const mapContainerStyle = {
+// height: '100%',
+// };
+
+// const defaultCenter = {
+// lat: 42.360081,
+// lng: -71.058884,
+// };
+
+// const mapOptions = {
+// fullscreenControl: false,
+// };
+
+// const apiKey = process.env.GOOGLE_MAPS;
+
+// const script = document.createElement('script');
+// script.defer = true;
+// script.async = true;
+// script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+// console.log(script.src);
+// document.head.appendChild(script);
+
+// /**
+// * Consider integrating later: allows for drawing, circling, making shapes on map
+// */
+// // const drawingManager = new window.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,
+// // ],
+// // },
+// // });
+
+// // options for searchbox in Google Maps Places Autocomplete API
+// 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 MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+// private _dropDisposer?: DragManager.DragDropDisposer;
+// private _disposers: { [name: string]: IReactionDisposer } = {};
+// private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+// @observable private _overlayAnnoInfo: Opt<Doc>;
+// showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+// public static LayoutString(fieldKey: string) {
+// return FieldView.LayoutString(MapBox2, fieldKey);
+// }
+// public get SidebarKey() {
+// return this.fieldKey + '_sidebar';
+// }
+// private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
+// @computed get inlineTextAnnotations() {
+// return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
+// }
+
+// @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
+// @observable private selectedPlace: Doc | undefined = undefined;
+// @observable private markerMap: { [id: string]: google.maps.Marker } = {};
+// @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
+// @observable private inputRef = React.createRef<HTMLInputElement>();
+// @observable private searchMarkers: google.maps.Marker[] = [];
+// @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+// @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+// @computed get allSidebarDocs() {
+// return DocListCast(this.dataDoc[this.SidebarKey]);
+// }
+// @computed get allMapMarkers() {
+// return DocListCast(this.dataDoc[this.annotationKey]);
+// }
+// @observable private toggleAddMarker = false;
+
+// @observable _showSidebar = false;
+// @computed get SidebarShown() {
+// return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
+// }
+
+// static _canAnnotate = true;
+// static _hadSelection: boolean = false;
+// private _sidebarRef = React.createRef<SidebarAnnos>();
+// private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+
+// componentDidMount() {
+// this.props.setContentView?.(this);
+// }
+
+// @action
+// private setSearchBox = (searchBox: any) => {
+// this.searchBox = searchBox;
+// };
+
+// // iterate allMarkers to size, center, and zoom map to contain all markers
+// private fitBounds = (map: google.maps.Map) => {
+// const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
+// const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+// !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+// };
+
+// /**
+// * Custom control for add marker button
+// * @param controlDiv
+// * @param map
+// */
+// private CenterControl = () => {
+// const controlDiv = document.createElement('div');
+// controlDiv.className = 'MapBox2-addMarker';
+// // Set CSS for the control border.
+// const controlUI = document.createElement('div');
+// controlUI.style.backgroundColor = '#fff';
+// controlUI.style.borderRadius = '3px';
+// controlUI.style.cursor = 'pointer';
+// controlUI.style.marginTop = '10px';
+// controlUI.style.borderRadius = '4px';
+// controlUI.style.marginBottom = '22px';
+// controlUI.style.textAlign = 'center';
+// controlUI.style.position = 'absolute';
+// controlUI.style.width = '32px';
+// controlUI.style.height = '32px';
+// controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
+
+// const plIcon = document.createElement('img');
+// plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+// plIcon.style.color = 'rgb(25,25,25)';
+// plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// plIcon.style.fontSize = '16px';
+// plIcon.style.lineHeight = '32px';
+// plIcon.style.left = '18';
+// plIcon.style.top = '15';
+// plIcon.style.position = 'absolute';
+// plIcon.width = 14;
+// plIcon.height = 14;
+// plIcon.innerHTML = 'Add';
+// controlUI.appendChild(plIcon);
+
+// // Set CSS for the control interior.
+// const markerIcon = document.createElement('img');
+// markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// markerIcon.style.fontSize = '16px';
+// markerIcon.style.lineHeight = '32px';
+// markerIcon.style.left = '-2';
+// markerIcon.style.top = '1';
+// markerIcon.width = 30;
+// markerIcon.height = 30;
+// markerIcon.style.position = 'absolute';
+// markerIcon.innerHTML = 'Add';
+// controlUI.appendChild(markerIcon);
+
+// // Setup the click event listeners
+// controlUI.addEventListener('click', () => {
+// if (this.toggleAddMarker === true) {
+// this.toggleAddMarker = false;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#fff';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// } else {
+// this.toggleAddMarker = true;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#4476f7';
+// markerIcon.style.color = 'rgb(255,255,255)';
+// }
+// });
+// controlDiv.appendChild(controlUI);
+// return controlDiv;
+// };
+
+// /**
+// * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
+// * @param position - the LatLng position where the marker is placed
+// * @param map
+// */
+// @action
+// private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+// const marker = new google.maps.Marker({
+// position: position,
+// map: map,
+// });
+// map.panTo(position);
+// const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+// this.addDocument(mapMarker, this.annotationKey);
+// };
+
+// _loadPending = true;
+// /**
+// * store a reference to google map instance
+// * setup the drawing manager on the top right corner of map
+// * fit map bounds to contain all markers
+// * @param map
+// */
+// @action
+// private loadHandler = (map: google.maps.Map) => {
+// this._map = map;
+// this._loadPending = true;
+// const centerControlDiv = this.CenterControl();
+// map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+// //drawingManager.setMap(map);
+// // if (navigator.geolocation) {
+// // navigator.geolocation.getCurrentPosition(
+// // (position: Position) => {
+// // const pos = {
+// // lat: position.coords.latitude,
+// // lng: position.coords.longitude,
+// // };
+// // this._map.setCenter(pos);
+// // }
+// // );
+// // } else {
+// // alert("Your geolocation is not supported by browser.")
+// // };
+// map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
+// map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
+// setTimeout(() => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// }, 250);
+// // listener to addmarker event
+// this._map.addListener('click', (e: MouseEvent) => {
+// if (this.toggleAddMarker === true) {
+// this.placeMarker((e as any).latLng, map);
+// }
+// });
+// };
+
+// @action
+// centered = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.mapLat = this._map.getCenter()?.lat();
+// this.dataDoc.mapLng = this._map.getCenter()?.lng();
+// };
+
+// @action
+// zoomChanged = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.map_zoom = this._map.getZoom();
+// };
+
+// /**
+// * Load and render all map markers
+// * @param marker
+// * @param place
+// */
+// @action
+// private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+// place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+// };
+
+// /**
+// * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
+// * @param e
+// * @param place
+// */
+// @action
+// private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
+// // set which place was clicked
+// this.selectedPlace = place;
+// place.infoWindowOpen = true;
+// };
+
+// /**
+// * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// console.log('print all sidebar Docs');
+// if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// docs.forEach(doc => {
+// if (doc.lat !== undefined && doc.lng !== undefined) {
+// const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
+// if (existingMarker) {
+// Doc.AddDocToList(existingMarker, 'data', doc);
+// } else {
+// const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+// this.addDocument(marker, this.annotationKey);
+// }
+// }
+// }); //add to annotation list
+
+// return this.addDocument(doc, sidebarKey); // add to sidebar list
+// };
+
+// /**
+// * Removing documents from the sidebar
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// return this.removeDocument(doc, sidebarKey);
+// };
+
+// /**
+// * Toggle sidebar onclick the tiny comment button on the top right corner
+// * @param e
+// */
+// sidebarBtnDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(
+// this,
+// e,
+// (e, down, delta) =>
+// runInAction(() => {
+// const localDelta = this.props
+// .ScreenToLocalTransform()
+// .scale(this.props.NativeDimScaling?.() || 1)
+// .transformDirection(delta[0], delta[1]);
+// const fullWidth = NumCast(this.layoutDoc._width);
+// const mapWidth = fullWidth - this.sidebarWidth();
+// if (this.sidebarWidth() + localDelta[0] > 0) {
+// this._showSidebar = true;
+// this.layoutDoc._width = fullWidth + localDelta[0];
+// this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+// } else {
+// this._showSidebar = false;
+// this.layoutDoc._width = mapWidth;
+// this.layoutDoc._layout_sidebarWidthPercent = '0%';
+// }
+// return false;
+// }),
+// emptyFunction,
+// () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
+// );
+// };
+
+// sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+// @computed get layout_sidebarWidthPercent() {
+// return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
+// }
+// @computed get sidebarColor() {
+// return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+// }
+
+// /**
+// * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+// * add a customized temporary marker on the map
+// */
+// @action
+// private handlePlaceChanged = () => {
+// const place = this.searchBox.getPlace();
+
+// if (!place.geometry || !place.geometry.location) {
+// // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
+// window.alert("No details available for input: '" + place.name + "'");
+// return;
+// }
+
+// // zoom in on the location of the search result
+// if (place.geometry.viewport) {
+// this._map.fitBounds(place.geometry.viewport);
+// } else {
+// this._map.setCenter(place.geometry.location);
+// this._map.setZoom(17);
+// }
+
+// // customize icon => customized icon for the nature of the location selected
+// const icon = {
+// url: place.icon as string,
+// size: new google.maps.Size(71, 71),
+// origin: new google.maps.Point(0, 0),
+// anchor: new google.maps.Point(17, 34),
+// scaledSize: new google.maps.Size(25, 25),
+// };
+
+// // put temporary cutomized marker on searched location
+// this.searchMarkers.forEach(marker => {
+// marker.setMap(null);
+// });
+// this.searchMarkers = [];
+// this.searchMarkers.push(
+// new window.google.maps.Marker({
+// map: this._map,
+// icon,
+// title: place.name,
+// position: place.geometry.location,
+// })
+// );
+// };
+
+// /**
+// * Handles toggle of sidebar on click the little comment button
+// */
+// @computed get sidebarHandle() {
+// return (
+// <div
+// className="MapBox2-overlayButton-sidebar"
+// key="sidebar"
+// title="Toggle Sidebar"
+// style={{
+// display: !this.props.isContentActive() ? 'none' : undefined,
+// top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
+// backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+// }}
+// onPointerDown={this.sidebarBtnDown}>
+// <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+// </div>
+// );
+// }
+
+// // TODO: Adding highlight box layer to Maps
+// @action
+// toggleSidebar = () => {
+// //1.2 * w * ? = .2 * w .2/1.2
+// const prevWidth = this.sidebarWidth();
+// this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+// this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+// };
+
+// sidebarDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
+// };
+// sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
+// const bounds = this._ref.current!.getBoundingClientRect();
+// this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+// this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
+// e.preventDefault();
+// return false;
+// };
+
+// setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+
+// addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+// return this.addDocument(doc, annotationKey);
+// };
+
+// pointerEvents = () => {
+// return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.IsDragging ? undefined : 'none';
+// };
+// @computed get annotationLayer() {
+// return (
+// <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+// {this.inlineTextAnnotations
+// .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+// .map(anno => (
+// <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+// ))}
+// </div>
+// );
+// }
+
+// getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document;
+
+// /**
+// * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
+// * @returns
+// */
+// private renderMarkers = () => {
+// return this.allMapMarkers.map(place => (
+// <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
+// ));
+// };
+
+// // TODO: auto center on select a document in the sidebar
+// private handleMapCenter = (map: google.maps.Map) => {
+// // console.log("print the selected views in selectionManager:")
+// // if (SelectionManager.Views.lastElement()) {
+// // console.log(SelectionManager.Views.lastElement());
+// // }
+// };
+
+// 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;
+
+// get MicrosoftMaps() {
+// return (window as any).Microsoft.Maps;
+// }
+// render() {
+// const renderAnnotations = (childFilters?: () => string[]) => null;
+// return (
+// <div className="MapBox2" ref={this._ref}>
+// <div
+// className="MapBox2-wrapper"
+// onWheel={e => e.stopPropagation()}
+// onPointerDown={async e => {
+// e.button === 0 && !e.ctrlKey && e.stopPropagation();
+// }}
+// style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+// <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+// {renderAnnotations(this.opaqueFilter)}
+// {SnappingManager.IsDragging ? null : renderAnnotations()}
+// {this.annotationLayer}
+
+// <div>
+// <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+// <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+// <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+// </Autocomplete>
+
+// {this.renderMarkers()}
+// {this.allMapMarkers
+// .filter(marker => marker.infoWindowOpen)
+// .map(marker => (
+// <MapBoxInfoWindow
+// key={marker[Id]}
+// {...this.props}
+// setContentView={emptyFunction}
+// place={marker}
+// markerMap={this.markerMap}
+// PanelWidth={this.infoWidth}
+// PanelHeight={this.infoHeight}
+// moveDocument={this.moveDocument}
+// isAnyChildContentActive={this.isAnyChildContentActive}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// />
+// ))}
+// {/* {this.handleMapCenter(this._map)} */}
+// </GoogleMap>
+// </div>
+// </div>
+// {/* </LoadScript > */}
+// <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+// <SidebarAnnos
+// ref={this._sidebarRef}
+// {...this.props}
+// fieldKey={this.fieldKey}
+// Document={this.Document}
+// layoutDoc={this.layoutDoc}
+// dataDoc={this.dataDoc}
+// usePanelWidth={true}
+// showSidebar={this.SidebarShown}
+// nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// PanelWidth={this.sidebarWidth}
+// sidebarAddDocument={this.sidebarAddDocument}
+// moveDocument={this.moveDocument}
+// removeDocument={this.sidebarRemoveDocument}
+// />
+// </div>
+// {this.sidebarHandle}
+// </div>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 66c47d131..a9c6ba22c 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,96 +1,95 @@
-import { InfoWindow } from '@react-google-maps/api';
-import { action } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
-import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { ViewBoxAnnotatableProps } from '../../DocComponent';
-import { FieldViewProps } from '../FieldView';
-import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import './MapBox.scss';
+// import { InfoWindow } from '@react-google-maps/api';
+// import { action } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+// import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
+// import { CollectionStackingView } from '../../collections/CollectionStackingView';
+// import { ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { FieldViewProps } from '../FieldView';
+// import { FormattedTextBox } from '../formattedText/FormattedTextBox';
+// import './MapBox.scss';
-interface MapBoxInfoWindowProps {
- place: Doc;
- renderDepth: number;
- markerMap: { [id: string]: google.maps.Marker };
- isAnyChildContentActive: () => boolean;
-}
-@observer
-export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
- @action
- private handleInfoWindowClose = () => {
- if (this.props.place.infoWindowOpen) {
- this.props.place.infoWindowOpen = false;
- }
- this.props.place.infoWindowOpen = false;
- };
+// interface MapBoxInfoWindowProps {
+// place: Doc;
+// renderDepth: number;
+// markerMap: { [id: string]: google.maps.Marker };
+// isAnyChildContentActive: () => boolean;
+// }
+// @observer
+// export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
+// @action
+// private handleInfoWindowClose = () => {
+// if (this.props.place.infoWindowOpen) {
+// this.props.place.infoWindowOpen = false;
+// }
+// this.props.place.infoWindowOpen = false;
+// };
- addNoteClick = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
- const newBox = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
- FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- Doc.AddDocToList(this.props.place, 'data', newBox);
- this._stack?.scrollToBottom();
- e.stopPropagation();
- e.preventDefault();
- });
- };
+// addNoteClick = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
+// const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
+// FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+// Doc.AddDocToList(this.props.place, 'data', newDoc);
+// this._stack?.scrollToBottom();
+// e.stopPropagation();
+// e.preventDefault();
+// });
+// };
- _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
- childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
- addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
- removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
- render() {
- return (
- <InfoWindow
- // anchor={this.props.markerMap[this.props.place[Id]]}
- onCloseClick={this.handleInfoWindowClose}>
- <div className="mapbox-infowindow">
- <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
- <CollectionStackingView
- ref={r => (this._stack = r)}
- {...this.props}
- setContentView={emptyFunction}
- Document={this.props.place}
- DataDoc={undefined}
- fieldKey="data"
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- childFilters={returnEmptyFilter}
- setHeight={emptyFunction}
- isAnnotationOverlay={false}
- select={emptyFunction}
- NativeDimScaling={returnOne}
- isContentActive={returnTrue}
- chromeHidden={true}
- rootSelected={returnFalse}
- childHideResizeHandles={returnTrue}
- childHideDecorationTitle={returnTrue}
- childLayoutFitWidth={this.childLayoutFitWidth}
- // childDocumentsActive={returnFalse}
- removeDocument={this.removeDoc}
- addDocument={this.addDoc}
- renderDepth={this.props.renderDepth + 1}
- type_collection={CollectionViewType.Stacking}
- pointerEvents={returnAll}
- />
- </div>
- <hr />
- <div
- onPointerDown={this.addNoteClick}
- onClick={e => {
- e.stopPropagation();
- e.preventDefault();
- }}>
- Add Note
- </div>
- </div>
- </InfoWindow>
- );
- }
-}
+// _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
+// childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+// addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
+// removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
+// render() {
+// return (
+// <InfoWindow
+// // anchor={this.props.markerMap[this.props.place[Id]]}
+// onCloseClick={this.handleInfoWindowClose}>
+// <div className="mapbox-infowindow">
+// <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+// <CollectionStackingView
+// ref={r => (this._stack = r)}
+// {...this.props}
+// setContentView={emptyFunction}
+// Document={this.props.place}
+// TemplateDataDocument={undefined}
+// fieldKey="data"
+// NativeWidth={returnZero}
+// NativeHeight={returnZero}
+// childFilters={returnEmptyFilter}
+// setHeight={emptyFunction}
+// isAnnotationOverlay={false}
+// select={emptyFunction}
+// NativeDimScaling={returnOne}
+// isContentActive={returnTrue}
+// chromeHidden={true}
+// childHideResizeHandles={true}
+// childHideDecorationTitle={true}
+// childLayoutFitWidth={this.childLayoutFitWidth}
+// // childDocumentsActive={returnFalse}
+// removeDocument={this.removeDoc}
+// addDocument={this.addDoc}
+// renderDepth={this.props.renderDepth + 1}
+// type_collection={CollectionViewType.Stacking}
+// pointerEvents={returnAll}
+// />
+// </div>
+// <hr />
+// <div
+// onPointerDown={this.addNoteClick}
+// onClick={e => {
+// e.stopPropagation();
+// e.preventDefault();
+// }}>
+// Add Note
+// </div>
+// </div>
+// </InfoWindow>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 56f0a49b8..8760c8600 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -1,15 +1,11 @@
-import { observer } from 'mobx-react';
-// import { SettingsManager } from '../../../util/SettingsManager';
+import * as React from 'react';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
-import React = require('react');
-import { computed } from 'mobx';
import { MapBox } from './MapBox';
/**
* Map Pushpin doc class
*/
-@observer
export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(MapPushpinBox, fieldKey);
@@ -21,11 +17,11 @@ export class MapPushpinBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.mapBoxView.deletePushpin(this.Document);
}
- @computed get mapBoxView() {
- return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as MapBox;
+ get mapBoxView() {
+ return this.props.DocumentView?.()?._props.docViewPath().lastElement()?.ComponentView as MapBox;
}
- @computed get mapBox() {
- return this.props.DocumentView?.().props.docViewPath().lastElement()?.Document;
+ get mapBox() {
+ return this.props.DocumentView?.()._props.docViewPath().lastElement()?.Document;
}
render() {