aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/MapBox/MapAnchorMenu.tsx')
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx401
1 files changed, 370 insertions, 31 deletions
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 66c4dc7b8..b1fb3368c 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,15 +1,27 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, makeObservable, reaction } from 'mobx';
-import { observer } from 'mobx-react';
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';
-import { Doc, Opt } from '../../../../fields/Doc';
import { SelectionManager } from '../../../util/SelectionManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
-import { IconButton } from 'browndash-components';
+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 { 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, ColorResult } from 'react-color';
+import { Position } from 'geojson';
+
+type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
@observer
export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -17,6 +29,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private _disposer: IReactionDisposer | undefined;
private _commentRef = React.createRef<HTMLDivElement>();
+ private _fileInputRef = React.createRef<HTMLInputElement>();
public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
@@ -30,6 +43,50 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// public MakeTargetToggle: () => void = unimplementedFunction;
// public ShowTargetTrail: () => void = unimplementedFunction;
public IsTargetToggler: () => boolean = returnFalse;
+
+ public DisplayRoute: (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => void = unimplementedFunction;
+ public HideRoute: () => void = unimplementedFunction;
+ public AddNewRouteToMap: (coordinates: Position[], origin: string, destination: any, createPinForDestination: boolean) => void = unimplementedFunction;
+ public CreatePin: (feature: any) => void = unimplementedFunction;
+
+ public UpdateMarkerColor: (color: string) => void = unimplementedFunction;
+ public UpdateMarkerIcon: (iconKey: string) => void = unimplementedFunction;
+
+ public OpenAnimationPanel: (routeDoc: Doc | undefined) => void = unimplementedFunction;
+
+ @observable
+ menuType: MapAnchorMenuType = 'standard';
+
+ @action
+ public setMenuType = (menuType: MapAnchorMenuType) => {
+ this.menuType = menuType;
+ };
+
+ private allMapPinDocs: Doc[] = [];
+
+ private pinDoc: Doc | undefined = undefined;
+
+ private routeDoc: Doc | undefined = undefined;
+
+ private title: string | undefined = undefined;
+
+ public setPinDoc(pinDoc: Doc) {
+ this.pinDoc = pinDoc;
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
+ }
+
+ public setRouteDoc(routeDoc: Doc) {
+ this.routeDoc = routeDoc;
+ 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;
}
@@ -42,6 +99,10 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
}
componentWillUnmount() {
+ this.destinationFeatures = [];
+ this.destinationSelected = false;
+ this.selectedDestinationFeature = undefined;
+ this.currentRouteInfoMap = undefined;
this._disposer?.();
}
@@ -81,39 +142,224 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
};
static top = React.createRef<HTMLDivElement>();
+
// public get Top(){
// 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: ColorResult) => {
+ if (this.pinDoc) {
+ this.pinDoc.markerColor = color.hex;
+ }
+ };
+
+ revertToOriginalMarker = () => {
+ if (this.pinDoc) {
+ this.pinDoc.markerType = 'MAP_PIN';
+ this.pinDoc.markerColor = '#ff5722';
+ }
+ };
+
+ onMarkerIconChange = (iconKey: string) => {
+ if (this.pinDoc) {
+ this.pinDoc.markerType = iconKey;
+ }
+ };
+
+ @observable
+ destinationFeatures: any[] = [];
+
+ @observable
+ destinationSelected: boolean = false;
+
+ @observable
+ selectedDestinationFeature: any = undefined;
+
+ @observable
+ createPinForDestination: boolean = true;
+
+ @observable
+ currentRouteInfoMap: Record<TransportationType, any> | undefined = undefined;
+
+ @observable
+ selectedTransportationType: TransportationType = 'driving';
+
+ @action
+ handleTransportationTypeChange = (newType: TransportationType) => {
+ 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) {
+ 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) {
+ 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
+ // create pin if createPinForDestination was clicked
+ };
+
+ HandleAddRouteClick = () => {
+ 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.HideRoute();
+ }
+ };
+
+ getMarkerIcon = (): JSX.Element | undefined => {
+ 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 = (
- <>
- {
- <IconButton
- tooltip="Delete Pin" //
- onPointerDown={this.Delete}
- icon={<FontAwesomeIcon icon="trash-alt" />}
- color={SettingsManager.userColor}
- />
- }
- {
- <div ref={this._commentRef}>
+ <div className="menu-buttons" style={{ display: 'flex' }}>
+ {this.menuType === 'standard' && (
+ <>
<IconButton
- tooltip="Link Note to Pin" //
- onPointerDown={this.notePointerDown}
- icon={<FontAwesomeIcon icon="sticky-note" />}
+ tooltip="Delete Pin" //
+ onPointerDown={this.Delete}
+ icon={<FontAwesomeIcon icon="trash-alt" />}
color={SettingsManager.userColor}
/>
- </div>
- }
- {
- <IconButton
- tooltip="Center on pin" //
- onPointerDown={this.Center}
- icon={<FontAwesomeIcon icon="compress-arrows-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" //
+ onPointerDown={this.notePointerDown}
+ icon={<FontAwesomeIcon icon="sticky-note" />}
+ color={SettingsManager.userColor}
+ />
+ </div>
+ <IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton
+ tooltip="Center on pin" //
+ onPointerDown={this.Center}
+ icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
+ color={SettingsManager.userColor}
+ />
+ </>
+ )}
+ {this.menuType === 'routeCreation' && (
+ <>
+ <IconButton
+ tooltip="Go back" //
+ onPointerDown={this.BackClick}
+ icon={<FontAwesomeIcon icon={faArrowLeft as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ <IconButton
+ tooltip="Add route" //
+ onPointerDown={this.HandleAddRouteClick}
+ icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ </>
+ )}
+ {this.menuType === 'route' && (
+ <>
+ <IconButton
+ tooltip="Delete Route" //
+ onPointerDown={this.Delete}
+ 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} />
+ <div ref={this._commentRef}>
+ <IconButton
+ tooltip="Link Note to Pin" //
+ onPointerDown={this.notePointerDown}
+ icon={<FontAwesomeIcon icon="sticky-note" />}
+ color={SettingsManager.userColor}
+ />
+ </div>
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
+ </>
+ )}
+ {this.menuType === 'customize' && (
+ <>
+ <IconButton
+ tooltip="Go back" //
+ onPointerDown={this.BackClick}
+ icon={<FontAwesomeIcon icon={faArrowLeft as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ <IconButton
+ tooltip="Revert to original" //
+ onPointerDown={() => this.revertToOriginalMarker()}
+ icon={<FontAwesomeIcon icon={faArrowsRotate as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ </>
+ )}
+
{/* {this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
@@ -125,13 +371,106 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
)} */}
- </>
+ </div>
);
+ // return (
+ // <div ref={MapAnchorMenu.top} style={{zIndex: 30000, width: '100%', height: '100px'}}>
+ // HELLO THIS IS ANCHOR MENU
+ // {this.getElement(buttons)}
+ // </div>
+ // )
return this.getElement(
- <div ref={MapAnchorMenu.top} style={{ width: '100%', display: 'flex' }}>
+ <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: any, searchText: any) => this.handleDestinationSearchChange(searchText)}
+ onChange={(e: any, feature: any, reason: any) => {
+ if (reason === 'clear') {
+ this.handleSelectedDestinationFeature(undefined);
+ } else if (reason === 'selectOption') {
+ this.handleSelectedDestinationFeature(feature);
+ }
+ }}
+ 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.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)}>
+ Get routes
+ </button>
+
+ {/* <input
+ placeholder="Origin"
+ /> */}
+ </div>
+ )}
+ {this.currentRouteInfoMap && (
+ <div className="current-route-info-container">
+ <div className="transportation-icons-container">
+ <IconButton
+ tooltip="Driving route"
+ onPointerDown={() => this.handleTransportationTypeChange('driving')}
+ icon={<FontAwesomeIcon icon={faCar as IconLookup} />}
+ color={this.selectedTransportationType === 'driving' ? 'lightblue' : 'grey'}
+ />
+ <IconButton
+ tooltip="Cycling route"
+ onPointerDown={() => this.handleTransportationTypeChange('cycling')}
+ icon={<FontAwesomeIcon icon={faBicycle as IconLookup} />}
+ color={this.selectedTransportationType === 'cycling' ? 'lightblue' : 'grey'}
+ />
+ <IconButton
+ tooltip="Walking route"
+ onPointerDown={() => this.handleTransportationTypeChange('walking')}
+ icon={<FontAwesomeIcon icon={faPersonWalking as IconLookup} />}
+ color={this.selectedTransportationType === 'walking' ? 'lightblue' : 'grey'}
+ />
+ </div>
+ <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()}</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>}
{buttons}
- </div>
+ </div>,
+ true
);
}
}