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.tsx385
1 files changed, 362 insertions, 23 deletions
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f6680aac0..fca3998c8 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,15 +1,39 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, Opt } from '../../../../fields/Doc';
+import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
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';
+
+type MapAnchorMenuType = 'standard' | 'route' | 'calendar' | 'customize';
@observer
export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -17,6 +41,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 +55,32 @@ 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: List<any>, origin: string, destination: string) => void = unimplementedFunction;
+ public CreatePin: (feature: any) => void = unimplementedFunction;
+
+
+ private allMapPinDocs: Doc[] = [];
+
+ private pinDoc: 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 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 +93,10 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
}
componentWillUnmount() {
+ this.destinationFeatures = [];
+ this.destinationSelected = false;
+ this.selectedDestinationFeature = undefined;
+ this.currentRouteInfoMap = undefined;
this._disposer?.();
}
@@ -81,39 +136,218 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
};
static top = React.createRef<HTMLDivElement>();
+
// public get Top(){
// return this.top
// }
+ @observable
+ menuType: MapAnchorMenuType = 'standard';
+
+ @action
+ DirectionsClick = () => {
+ this.menuType = 'route';
+ }
+
+ @action
+ CustomizeClick = () => {
+ this.menuType = 'customize';
+ }
+
+ @action
+ BackClick = () => {
+ this.menuType = 'standard';
+ }
+
+ @action
+ TriggerFileInputClick = () => {
+ if (this._fileInputRef) {
+ this._fileInputRef.current?.click(); // Trigger the file input click event
+ }
+ }
+
+
+ @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;
+ this.AddNewRouteToMap(this.currentRouteInfoMap![this.selectedTransportationType].coordinates, this.title ?? "", this.selectedDestinationFeature.place_name);
+ this.HideRoute();
+ }
+ }
+
+
render() {
const buttons = (
- <>
- {
- <IconButton
+ <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}/>}
+ 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}
+ />
+ </>
}
- {
- <div ref={this._commentRef}>
+ {this.menuType === 'route' &&
+ <>
<IconButton
- tooltip="Link Note to Pin" //
- onPointerDown={this.notePointerDown}
- icon={<FontAwesomeIcon icon="sticky-note" />}
+ tooltip="Go back" //
+ onPointerDown={this.BackClick}
+ icon={<FontAwesomeIcon icon={faArrowLeft as IconLookup} />}
color={SettingsManager.userColor}
/>
- </div>
+ <IconButton
+ tooltip="Add route" //
+ onPointerDown={this.HandleAddRouteClick}
+ 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="Center on pin" //
- onPointerDown={this.Center}
- icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
- color={SettingsManager.userColor}
- />
+ {this.menuType === 'customize' &&
+ <>
+ <IconButton
+ tooltip="Go back" //
+ onPointerDown={this.BackClick}
+ icon={<FontAwesomeIcon icon={faArrowLeft as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ <IconButton
+ tooltip="Upload image" //
+ onPointerDown={this.TriggerFileInputClick}
+ icon={<FontAwesomeIcon icon={faUpload as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ <input
+ type="file"
+ accept="image/*" // Optionally, specify accepted file types
+ ref={this._fileInputRef}
+ style={{ display: "none" }}
+ onChange={() => {}}
+ />
+ <IconButton
+ tooltip="Revert to original" //
+ onPointerDown={this.BackClick}
+ icon={<FontAwesomeIcon icon={faArrowsRotate as IconLookup} />}
+ color={SettingsManager.userColor}
+ />
+ </>
}
+
+
{/* {this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
@@ -125,13 +359,118 @@ 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 === 'route' &&
+ <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'){
+ this.handleSelectedDestinationFeature(undefined);
+ } 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'
+ />
+ )}
+ />
+ {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>
+
+
+ }
{buttons}
</div>
- );
+ , true);
}
}