diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionMapView.tsx | 127 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/MapView/CollectionMapView.scss (renamed from src/client/views/collections/CollectionMapView.scss) | 18 | ||||
-rw-r--r-- | src/client/views/collections/MapView/CollectionMapView.tsx | 312 | ||||
-rw-r--r-- | src/client/views/nodes/PresBox.tsx | 13 | ||||
-rw-r--r-- | src/client/views/presentationview/PresElementBox.tsx | 4 |
6 files changed, 346 insertions, 130 deletions
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx deleted file mode 100644 index 07755ed71..000000000 --- a/src/client/views/collections/CollectionMapView.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { GoogleMap, Marker, InfoWindow, LoadScript, GoogleMapProps } from '@react-google-maps/api'; -import { action, computed, Lambda, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../fields/Doc"; -import { documentSchema } from "../../../fields/documentSchemas"; -import { Id } from "../../../fields/FieldSymbols"; -import { makeInterface } from "../../../fields/Schema"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; -import { LinkManager } from "../../util/LinkManager"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; -import "./CollectionMapView.scss"; -import { CollectionSubView } from "./CollectionSubView"; -import React = require("react"); -import requestPromise = require("request-promise"); - -type MapSchema = makeInterface<[typeof documentSchema]>; -const MapSchema = makeInterface(documentSchema); - -export type LocationData = google.maps.LatLngLiteral & { - address?: string - resolvedAddress?: string; - zoom?: number; -}; - -const mapContainerStyle = { - height: '100%', -}; - - -const defaultCenter = { - lat: 0, - lng: 0, -} -//-----------------------------test map marker----------------------------------- -// const map = new google.maps.Map(document.getElementById('map')!, { -// zoom: 10, -// center: new google.maps.LatLng(-33.92, 151.25), -// mapTypeId: google.maps.MapTypeId.ROADMAP -// }); - -// // test display markers -// let locations = [ -// ['Bondi Beach', -33.890542, 151.274856, 4], -// ['Coogee Beach', -33.923036, 151.259052, 5], -// ['Cronulla Beach', -34.028249, 151.157507, 3], -// ['Manly Beach', -33.80010128657071, 151.28747820854187, 2], -// ['Maroubra Beach', -33.950198, 151.259302, 1] -// ]; - -// const infowindow = new google.maps.InfoWindow(); -// let marker: google.maps.Marker -// let i: number - -// for (i = 0; i < locations.length; i++) { -// marker = new google.maps.Marker({ -// position: new google.maps.LatLng(locations[i][1] as number, locations[i][2] as number), -// map: map -// }); - -// google.maps.event.addListener(marker, 'click', (function (marker, i) { -// return function () { -// infowindow.setContent(locations[i][0] as string); -// infowindow.open(map, marker); -// } -// })(marker, i)); -// } - -//---------------------------------------------------------------- - -@observer -export default class CollectionMapView extends CollectionSubView<MapSchema, GoogleMapProps>(MapSchema) { - - - render() { - const { childLayoutPairs } = this; - const { Document, fieldKey, isContentActive: active } = this.props; - - // const { isLoaded, loadError } = useLoadScript({ - // googleMapsApiKey: 'AIzaSyALJU8DfCAqEAS0OqMDCmkE0otlz4H81fg', - // libraries: ['places'] - // }) - - // if (loadError) return "Error loading maps"; - // if (!isLoaded) return "Loading Maps"; - - - return <div className="collectionMapView" ref={this.createDashEventsTarget}> - - <div className={"collectionMapView-contents"} - style={{ pointerEvents: active() ? undefined : "none", overflow: 'hidden' }} - onWheel={e => e.stopPropagation()} - onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > - <LoadScript - googleMapsApiKey={process.env.GOOGLE_MAPS!} - libraries={['places']} - > - <div className="map-wrapper"> - <GoogleMap - mapContainerStyle={mapContainerStyle} - zoom={5} - center={defaultCenter} - > - <Marker - // name={'SOMA'} - position={{ lat: 37.778519, lng: -122.405640 }} /> - <Marker - // name={'Dolores park'} - position={{ lat: 47.617701, lng: -122.359485 }} /> - </GoogleMap> - </div> - </LoadScript > - </div> - </div >; - } - -} - -// export default GoogleApiWrapper({ -// // apiKey: process.env.REACT_APP_DASH_GOOGLE_MAPS_API_KEY!, -// apiKey: 'AIzaSyALJU8DfCAqEAS0OqMDCmkE0otlz4H81fg', -// LoadingContainer: () => { -// console.log(process.env.REACT_APP_DASH_GOOGLE_MAPS_API_KEY); -// return <div className={"loadingWrapper"}> -// <img className={"loadingGif"} src={"/assets/loading.gif"} /> -// </div>; -// } -// })(CollectionMapView) as any;
\ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index fb60265e3..65188e6f3 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -25,7 +25,7 @@ import { CollectionDockingView } from "./CollectionDockingView"; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionGridView } from './collectionGrid/CollectionGridView'; import { CollectionLinearView } from './CollectionLinearView'; -import CollectionMapView from './CollectionMapView'; +import CollectionMapView from './MapView/CollectionMapView'; import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView'; import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView'; import { CollectionPileView } from './CollectionPileView'; diff --git a/src/client/views/collections/CollectionMapView.scss b/src/client/views/collections/MapView/CollectionMapView.scss index 874511533..c76f1d4b9 100644 --- a/src/client/views/collections/CollectionMapView.scss +++ b/src/client/views/collections/MapView/CollectionMapView.scss @@ -10,6 +10,24 @@ > div { position: unset !important; // when the sidebar filter flys out, this prevents the map from extending outside the document box } + + .map-wrapper { + .searchbox { + box-sizing: border-box; + border: 1px solid transparent; + width: 240px; + height: 32px; + padding: 0 12px; + border-radius: 3px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + font-size: 14px; + outline: none; + text-overflow: ellipses; + position: absolute; + left: 50%; + margin-left: -120px; + } + } } } diff --git a/src/client/views/collections/MapView/CollectionMapView.tsx b/src/client/views/collections/MapView/CollectionMapView.tsx new file mode 100644 index 000000000..b3a5bdbff --- /dev/null +++ b/src/client/views/collections/MapView/CollectionMapView.tsx @@ -0,0 +1,312 @@ +import { GoogleMap, Marker, InfoWindow, InfoBox, LoadScript, GoogleMapProps, StandaloneSearchBox, DrawingManager } from '@react-google-maps/api'; +import { observable, action, computed, Lambda, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../../fields/Doc"; +import { documentSchema } from "../../../../fields/documentSchemas"; +import { Id } from "../../../../fields/FieldSymbols"; +import { makeInterface } from "../../../../fields/Schema"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; +import { LinkManager } from "../../../util/LinkManager"; +import { undoBatch, UndoManager } from "../../../util/UndoManager"; +import "./CollectionMapView.scss"; +import { CollectionSubView } from "../CollectionSubView"; +import React = require("react"); +import requestPromise = require("request-promise"); +import ReactDOM from 'react-dom'; + + +type MapSchema = makeInterface<[typeof documentSchema]>; +const MapSchema = makeInterface(documentSchema); + +export type LocationData = { + id?: number; + pos?: { lat: number, lng: number }; +}; + +const mapContainerStyle = { + height: '100%', +}; + +const defaultCenter = { + lat: 38.685, + lng: -115.234, +}; + +@observer +export default class CollectionMapView extends CollectionSubView<MapSchema, GoogleMapProps>(MapSchema) { + + @observable private mapRef = null as any; + @observable private selectedPlace: LocationData = { id: undefined, pos: undefined }; + @observable private markerMap = {}; + @observable private center = defaultCenter; + @observable private zoom = 2.5; + @observable private clickedLatLng = null; + @observable private infoWindowOpen = false; + + @observable private myPlaces = [ + { id: 1, pos: { lat: 39.09366509575983, lng: -94.58751660204751 } }, + { id: 2, pos: { lat: 41.82399, lng: -71.41283 } }, + { id: 3, pos: { lat: 47.606214, lng: -122.33207 } }, + { id: 4, pos: { lat: 36.7783, lng: 119.4179 } } + ]; + + // iterate myPlaces to size, center, and zoom map to contain all markers + private fitBounds = (map: any) => { + const bounds = new window.google.maps.LatLngBounds(); + console.log('map bound is:' + bounds); + this.myPlaces.map(place => { + bounds.extend(place.pos); + return place.id; + }); + map.fitBounds(bounds) + } + + // store a reference to google map instance; fit map bounds to contain all markers + @action + private loadHandler = (map: any) => { + this.mapRef = map; + this.fitBounds(map); + + // //add a custom control for button add marker + // //TODO: why this doesn't work + // const google = window.google; + // console.log("google window: " + google) + // const controlButtonDiv = document.createElement('div'); + // ReactDOM.render(<button onClick={() => console.log('click me to add marker!')}>Add Marker</button>, controlButtonDiv); + // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlButtonDiv); + } + + @action + private markerClickHandler = (e: MouseEvent, place: LocationData) => { + // set which place was clicked + this.selectedPlace = place + + console.log(this.selectedPlace.id); + console.log(this.selectedPlace.pos); + + // used so clicking a second marker works + if (this.infoWindowOpen) { + this.infoWindowOpen = false; + } + + this.infoWindowOpen = true; + } + + @action + private handleInfoWindowClose = () => { + if (this.infoWindowOpen) { + this.infoWindowOpen = false; + } + this.infoWindowOpen = false; + this.selectedPlace = { id: undefined, pos: undefined }; + } + + // @action + // private markerLoadHandler = (marker: any, place: LocationData) => { + // if (marker != null) { + // this.markerMap[place.id] = marker; + // } + // } + + @action + @undoBatch + private handleDragMarker = (marker: any, place: LocationData) => { + if (marker != null) { + place = { + id: place.id, + pos: { + lat: marker.latLng.lat().toFixed(3), + lng: marker.latLng.lng().toFixed(3) + } + } + + console.log(place); + console.log(this.myPlaces); + } + } + + /** + * func that handles user's search location and zoom/pan on the input location + */ + private searchLocation = () => { + + } + + /** + * func that adds marker to google maps + */ + @undoBatch + private addMarker = () => { + + } + + /** + * func that updates suggested results (and auto-complete) during user input + */ + private handleChange = () => { + + } + + // private mapRef = document.getElementById("map") + // private markers: google.maps.Marker[] = []; + @observable private input: HTMLInputElement | undefined; + + @action + private setInputRef = (element: HTMLInputElement) => { + this.input = element; + console.log("htmlinputelement is:" + this.input) + } + // private searchBox = new window.google.maps.places.SearchBox(this.input); + + private handlePlacesChanged = () => { + const searchBox = new window.google.maps.places.SearchBox(this.input!) + const places = searchBox.getPlaces(); + + console.log(places); + + if (places.length == 0) { return; } + + // // clear out old markers + // this.markers.forEach((marker) => { + // marker.setMap(null); + // }); + // this.markers = [] + + // // for each place, get icon, name, location + const bounds = new window.google.maps.LatLngBounds(); + places.forEach((place) => { + if (!place.geometry || !place.geometry.location) { + console.log("Returned place contains no geometry"); + return; + } + + // 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), + // } + + // //create a marker for each place + // this.markers.push( + // new google.maps.Marker({ + // mapRef, + // title: place.name, + // position: place.geometry.location, + // }) + // ); + + if (place.geometry.viewport) { + // only geocodes have viewport + bounds.union(place.geometry.viewport); + } + else { + bounds.extend(place.geometry.location); + } + }); + } + + // private setBounds = () => { + // const places = this.searchBox.getPlaces(); + // if (places.length == 0) { return; } + + // const bounds = new google.maps.LatLngBounds(); + // places.forEach((place) => { + // if (!place.geometry || !place.geometry.location) { + // console.log("Returned place contains no geometry"); + // return; + // } + + // if (place.geometry.viewport) { + // // only geocodes have viewport + // bounds.union(place.geometry.viewport); + // } + // else { + // bounds.extend(place.geometry.location); + // } + // }); + + // return bounds + // } + + private onPolygonComplete = (polygon: any) => { + console.log(polygon) + } + + private onMarkerComplete = (marker: any) => { + this.myPlaces.push(marker) + } + + render() { + const { Document, fieldKey, isContentActive: active } = this.props; + + return <div className="collectionMapView" ref={this.createDashEventsTarget}> + + <div className={"collectionMapView-contents"} + style={{ pointerEvents: active() ? undefined : "none", overflow: 'hidden' }} + onWheel={e => e.stopPropagation()} + onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} > + <LoadScript + googleMapsApiKey={process.env.GOOGLE_MAPS!} + libraries={['places', 'drawing']} + > + <div className="map-wrapper"> + <GoogleMap + mapContainerStyle={mapContainerStyle} + zoom={this.zoom} + center={this.center} + onLoad={map => this.loadHandler(map)} + > + <StandaloneSearchBox + onPlacesChanged={this.handlePlacesChanged}> + <input ref={this.setInputRef} className="searchbox" type="text" placeholder="Search anywhere:" /> + </StandaloneSearchBox> + + {this.myPlaces.map(place => ( + <Marker + key={place.id} + position={place.pos} + // onLoad={marker => this.markerLoadHandler(marker, place)} + onClick={e => this.markerClickHandler(e, place)} + draggable={true} + onDragEnd={marker => this.handleDragMarker(marker, place)} + /> + ))} + {this.infoWindowOpen && this.selectedPlace && ( + <InfoWindow + // anchor={this.markerMap[this.selectedPlace.id]} + onCloseClick={this.handleInfoWindowClose} + position={this.selectedPlace.pos} + // options={{ enableEventPropagation: true }} + > + <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}> + <div style={{ fontSize: 16 }}> + <div> + <a>a link to another node</a> + </div> + <div> + <a>a link to another node</a> + </div> + <div> + <a>a link to another node</a> + </div> + <div> + <button>New +</button> + </div> + </div> + </div> + </InfoWindow> + )} + {/* <DrawingManager + onPolygonComplete={this.onPolygonComplete} + onMarkerComplete={this.onMarkerComplete} + /> */} + </GoogleMap> + </div> + </LoadScript > + </div > + </div >; + } + +}
\ No newline at end of file diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f3fb6ff17..693348613 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -80,7 +80,12 @@ const PresBoxDocument = makeInterface(documentSchema); @observer export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } - + + /** + * transitions & effects for documents + * @param renderDoc + * @param layoutDoc + */ static renderEffectsDoc(renderDoc: any, layoutDoc: Doc) { const effectProps = { left: layoutDoc.presEffectDirection === PresEffect.Left, @@ -258,6 +263,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } } + //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // No more frames in current doc and next slide is defined, therefore move to next slide nextSlide = (activeNext: Doc) => { const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null); @@ -453,7 +459,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> /** * Uses the viewfinder to progressivize through the different views of a single collection. - * @param presTargetDoc: document for which internal zoom is used + * @param activeItem: document for which internal zoom is used */ zoomProgressivizeNext = (activeItem: Doc) => { const targetDoc: Doc = this.targetDoc; @@ -569,6 +575,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> load(); } + // The function pauses the auto presentation @action pauseAutoPres = () => { if (this.layoutDoc.presStatus === PresStatus.Autoplay) { @@ -592,6 +599,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> }); } + // The function allows for viewing the pres path on toggle @action togglePath = (srcContext: Doc, off?: boolean) => { if (off) { this._pathBoolean = false; @@ -602,6 +610,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } } + // The function allows for expanding the view of pres on toggle @action toggleExpandMode = () => { runInAction(() => this._expandBoolean = !this._expandBoolean); this.rootDoc.expandBoolean = this._expandBoolean; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index f15d51764..19893af7e 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -164,6 +164,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc e.preventDefault(); } + /** + * Function to drag and drop the pres element to a diferent location + */ startDrag = (e: PointerEvent) => { const miniView: boolean = this.toolbarWidth <= 100; const activeItem = this.rootDoc; @@ -242,6 +245,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc e.stopPropagation(); }); + // set the value/title of the individual pres element @undoBatch @action onSetValue = (value: string) => { |