diff options
Diffstat (limited to 'src/client/views/nodes/MapBox/MapAnchorMenu.tsx')
-rw-r--r-- | src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 385 |
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); } } |