aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionMapView.tsx269
-rw-r--r--src/client/views/collections/CollectionMenu.tsx7
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/MapView/CollectionMapView.scss (renamed from src/client/views/collections/CollectionMapView.scss)23
-rw-r--r--src/client/views/collections/MapView/CollectionMapView.tsx272
-rw-r--r--src/client/views/collections/TabDocView.scss5
-rw-r--r--src/client/views/collections/TabDocView.tsx2
7 files changed, 302 insertions, 278 deletions
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
deleted file mode 100644
index 2d7569d45..000000000
--- a/src/client/views/collections/CollectionMapView.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import { GoogleApiWrapper, IMapProps, Map as GeoMap, Marker } from "google-maps-react";
-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;
-};
-
-interface DocLatLng {
- lat: FieldResult<Field>;
- lng: FieldResult<Field>;
-}
-
-// Nowhere, Oklahoma
-const defaultLocation = { lat: 35.1592238, lng: -98.444512, zoom: 15 };
-const noResults = "ZERO_RESULTS";
-
-const query = async (data: string | google.maps.LatLngLiteral) => {
- const contents = typeof data === "string" ? `address=${data.replace(/\s+/g, "+")}` : `latlng=${data.lat},${data.lng}`;
- const target = `https://maps.googleapis.com/maps/api/geocode/json?${contents}&key=${process.env.GOOGLE_MAPS_GEO}`;
- try {
- return JSON.parse(await requestPromise.get(target));
- } catch {
- return undefined;
- }
-};
-
-@observer
-export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMapProps> & { google: any }>(MapSchema) {
-
- private _cancelAddrReq = new Map<string, boolean>();
- private _cancelLocReq = new Map<string, boolean>();
- private _initialLookupPending = new Map<string, boolean>();
- private responders: { locationDisposer: Lambda, addressDisposer: Lambda }[] = [];
-
- /**
- * Note that all the uses of runInAction below are not included
- * as a way to update observables (documents handle this already
- * in their property setters), but rather to create a single bulk
- * update and thus prevent uneeded invocations of the location-
- * and address–updating reactions.
- */
-
- private getLocation = (doc: Opt<Doc>, fieldKey: string, returnDefault: boolean = true): Opt<LocationData> => {
- if (doc) {
- const titleLoc = StrCast(doc.title).startsWith("@") ? StrCast(doc.title).substring(1) : undefined;
- const lat = Cast(doc[`${fieldKey}-lat`], "number", null) || (Cast(doc[`${fieldKey}-lat`], "string", null) && Number(Cast(doc[`${fieldKey}-lat`], "string", null))) || undefined;
- const lng = Cast(doc[`${fieldKey}-lng`], "number", null) || (Cast(doc[`${fieldKey}-lng`], "string", null) && Number(Cast(doc[`${fieldKey}-lng`], "string", null))) || undefined;
- const zoom = Cast(doc[`${fieldKey}-zoom`], "number", null) || (Cast(doc[`${fieldKey}-zoom`], "string", null) && Number(Cast(doc[`${fieldKey}-zoom`], "string", null))) || undefined;
- const address = titleLoc || StrCast(doc[`${fieldKey}-address`], StrCast(doc.title).replace(/^-/, ""));
- if (titleLoc || (address && (lat === undefined || lng === undefined))) {
- const id = doc[Id];
- if (!this._initialLookupPending.get(id)) {
- this._initialLookupPending.set(id, true);
- setTimeout(() => {
- titleLoc && Doc.SetInPlace(doc, `${fieldKey}-address`, titleLoc, true);
- this.respondToAddressChange(doc, fieldKey, address).then(() => this._initialLookupPending.delete(id));
- });
- }
- }
- return (lat === undefined || lng === undefined) ? (returnDefault ? defaultLocation : undefined) : { lat, lng, zoom };
- }
- return undefined;
- }
-
- private markerClick = async (layout: Doc, { lat, lng, zoom }: LocationData) => {
- const batch = UndoManager.StartBatch("marker click");
- const { fieldKey } = this.props;
- runInAction(() => {
- this.layoutDoc[`${fieldKey}-mapCenter-lat`] = lat;
- this.layoutDoc[`${fieldKey}-mapCenter-lng`] = lng;
- zoom && (this.layoutDoc[`${fieldKey}-mapCenter-zoom`] = zoom);
- });
- if (layout.isLinkButton && DocListCast(layout.links).length) {
- await LinkManager.traverseLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
- this.props.addDocTab(doc, where);
- finished?.();
- }, false, this.props.ContainingCollectionDoc, batch.end, undefined);
- } else {
- ScriptCast(layout.onClick)?.script.run({ this: layout, self: Cast(layout.rootDocument, Doc, null) || layout });
- batch.end();
- }
- }
-
- private renderMarkerIcon = (layout: Doc) => {
- const { Document } = this.props;
- const fieldKey = Doc.LayoutFieldKey(layout);
- const iconUrl = StrCast(layout.mapIconUrl, StrCast(Document.mapIconUrl));
- if (iconUrl) {
- const iconWidth = NumCast(layout[`${fieldKey}-iconWidth`], 45);
- const iconHeight = NumCast(layout[`${fieldKey}-iconHeight`], 45);
- const iconSize = new google.maps.Size(iconWidth, iconHeight);
- return {
- size: iconSize,
- scaledSize: iconSize,
- url: iconUrl
- };
- }
- }
-
- private renderMarker = (layout: Doc, fieldKey?: string) => {
- const location = this.getLocation(layout, fieldKey || Doc.LayoutFieldKey(layout));
- return !location ? (null) :
- <Marker
- key={layout[Id]}
- label={StrCast(layout[`${this.props.fieldKey}-address`])}
- position={location}
- onClick={() => this.markerClick(layout, location)}
- icon={this.renderMarkerIcon(layout)}
- />;
- }
-
- private respondToAddressChange = async (doc: Doc, fieldKey: string, newAddress: string, oldAddress?: string) => {
- if (newAddress === oldAddress) {
- return false;
- }
- const response = await query(newAddress);
- const id = doc[Id];
- if (!response || response.status === noResults) {
- this._cancelAddrReq.set(id, true);
- doc[`${fieldKey}-address`] = oldAddress;
- return false;
- }
- const { geometry, formatted_address } = response.results[0];
- const { lat, lng } = geometry.location;
- runInAction(() => {
- if (doc[`${fieldKey}-lat`] !== lat || doc[`${fieldKey}-lng`] !== lng) {
- this._cancelLocReq.set(id, true);
- Doc.SetInPlace(doc, `${fieldKey}-lat`, lat, true);
- Doc.SetInPlace(doc, `${fieldKey}-lng`, lng, true);
- }
- if (formatted_address !== newAddress) {
- this._cancelAddrReq.set(id, true);
- Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
- }
- });
- return true;
- }
-
- private respondToLocationChange = async (doc: Doc, fieldKey: string, newLatLng: DocLatLng, oldLatLng: Opt<DocLatLng>) => {
- if (newLatLng === oldLatLng) {
- return false;
- }
- const response = await query({ lat: NumCast(newLatLng.lat), lng: NumCast(newLatLng.lng) });
- const id = doc[Id];
- if (!response || response.status === noResults) {
- this._cancelLocReq.set(id, true);
- runInAction(() => {
- doc[`${fieldKey}-lat`] = oldLatLng?.lat;
- doc[`${fieldKey}-lng`] = oldLatLng?.lng;
- });
- return false;
- }
- const { formatted_address } = response.results[0];
- if (formatted_address !== doc[`${fieldKey}-address`]) {
- this._cancelAddrReq.set(doc[Id], true);
- Doc.SetInPlace(doc, `${fieldKey}-address`, formatted_address, true);
- }
- return true;
- }
-
- @computed get reactiveContents() {
- this.responders.forEach(({ locationDisposer, addressDisposer }) => {
- locationDisposer();
- addressDisposer();
- });
- this.responders = [];
- return this.childLayoutPairs.map(({ layout }) => {
- const fieldKey = Doc.LayoutFieldKey(layout);
- const id = layout[Id];
- this.responders.push({
- locationDisposer: computed(() => ({ lat: layout[`${fieldKey}-lat`], lng: layout[`${fieldKey}-lng`] }))
- .observe(({ oldValue, newValue }) => {
- if (this._cancelLocReq.get(id)) {
- this._cancelLocReq.set(id, false);
- } else if (newValue.lat !== undefined && newValue.lng !== undefined) {
- this.respondToLocationChange(layout, fieldKey, newValue, oldValue);
- }
- }),
- addressDisposer: computed(() => Cast(layout[`${fieldKey}-address`], "string", null))
- .observe(({ oldValue, newValue }) => {
- if (this._cancelAddrReq.get(id)) {
- this._cancelAddrReq.set(id, false);
- } else if (newValue?.length) {
- this.respondToAddressChange(layout, fieldKey, newValue, oldValue);
- }
- })
- });
- return this.renderMarker(layout);
- });
- }
-
- render() {
- const { childLayoutPairs } = this;
- const { Document, fieldKey, isContentActive: active, google } = this.props;
- const mapLoc = this.getLocation(this.rootDoc, `${fieldKey}-mapCenter`, false);
- let center = mapLoc;
- if (center === undefined) {
- const childLocations = childLayoutPairs.map(({ layout }) => this.getLocation(layout, Doc.LayoutFieldKey(layout), false));
- center = childLocations.find(location => location) || defaultLocation;
- }
- return <div className="collectionMapView" ref={this.createDashEventsTarget}>
- <div className={"collectionMapView-contents"}
- style={{ pointerEvents: active() ? undefined : "none" }}
- onWheel={e => e.stopPropagation()}
- onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
- <GeoMap
- google={google}
- zoom={center.zoom || 10}
- initialCenter={center}
- center={center}
- onIdle={(_props?: IMapProps, map?: google.maps.Map) => {
- if (this.layoutDoc._lockedTransform) {
- // reset zoom (ideally, we could probably can tell the map to disallow zooming somehow instead)
- map?.setZoom(center?.zoom || 10);
- map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
- } else {
- const zoom = map?.getZoom();
- (center?.zoom !== zoom) && undoBatch(action(() => {
- Document[`${fieldKey}-mapCenter-zoom`] = zoom;
- }))();
- }
- }}
- onDragend={(_props?: IMapProps, map?: google.maps.Map) => {
- if (this.layoutDoc._lockedTransform) {
- // reset the drag (ideally, we could probably can tell the map to disallow dragging somehow instead)
- map?.setCenter({ lat: center?.lat!, lng: center?.lng! });
- } else {
- undoBatch(action(({ lat, lng }) => {
- Document[`${fieldKey}-mapCenter-lat`] = lat();
- Document[`${fieldKey}-mapCenter-lng`] = lng();
- }))(map?.getCenter());
- }
- }}
- >
- {this.reactiveContents}
- {mapLoc && StrCast(this.rootDoc[`${fieldKey}-mapCenter-address`]) ? this.renderMarker(this.rootDoc, `${fieldKey}-mapCenter`) : undefined}
- </GeoMap>
- </div>
- </div>;
- }
-
-}
-
-export default GoogleApiWrapper({
- apiKey: process.env.GOOGLE_MAPS!,
- LoadingContainer: () => {
- console.log(process.env.GOOGLE_MAPS);
- 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/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 8f4df4a92..a5f54c342 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -278,6 +278,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
case DocumentType.WEB: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
case DocumentType.VID: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
case DocumentType.RTF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />);
+ case DocumentType.MAP: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
}
}
@@ -433,7 +434,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
const scroll = targetDoc._scrollTop;
activeDoc.presPinView = true;
activeDoc.presPinViewScroll = scroll;
- } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
+ } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) {
const x = targetDoc._panX;
const y = targetDoc._panY;
const scale = targetDoc._viewScale;
@@ -720,7 +721,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}>
- </button>
+ </button>
</Tooltip>)}
</div>;
}
@@ -991,7 +992,7 @@ export class CollectionTreeViewChrome extends React.Component<CollectionMenuProp
<button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
<div className="collectionTreeViewChrome-sortLabel">
Sort
- </div>
+ </div>
<div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}>
<FontAwesomeIcon icon="caret-up" size="2x" color="white" />
</div>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ae06d2f4..25a37ed71 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 870b7fda8..0dc226c04 100644
--- a/src/client/views/collections/CollectionMapView.scss
+++ b/src/client/views/collections/MapView/CollectionMapView.scss
@@ -1,13 +1,34 @@
.collectionMapView {
width: 100%;
height: 100%;
+ overflow: hidden;
.collectionMapView-contents {
width: 100%;
height: 100%;
+ overflow: hidden;
> 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: 300px;
+ 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;
+ margin-top: 5px;
+ }
+ }
}
}
@@ -27,4 +48,4 @@
width: 50px;
height: 50px;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/MapView/CollectionMapView.tsx b/src/client/views/collections/MapView/CollectionMapView.tsx
new file mode 100644
index 000000000..1166de61c
--- /dev/null
+++ b/src/client/views/collections/MapView/CollectionMapView.tsx
@@ -0,0 +1,272 @@
+import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, Autocomplete } from '@react-google-maps/api';
+import { observable, action, computed, Lambda, runInAction, IReactionDisposer } 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';
+import { DragManager } from '../../../util/DragManager';
+import { MapMarker } from '../../nodes/MapBox/MapMarker';
+
+
+/**
+ * Idea behind storing a marker:
+ * 1. on the map api, have a button "add marker" that adds the marker on the map & store the marker as a node in the collection
+ * (but don't render the marker in the collection itself)
+ * 2. each marker, as a node, has the same feature as all other nodes for linking (the same way one could form a link between a node inside a child collection
+ * and a node outside the child collection)
+ *
+ * /util/LinkManager.ts -- link relations
+ *
+ */
+
+type MapSchema = makeInterface<[typeof documentSchema]>;
+const MapSchema = makeInterface(documentSchema);
+
+export type Coordinates = {
+ lat: number,
+ lng: number,
+}
+
+export type LocationData = {
+ id: string;
+ pos: Coordinates;
+};
+
+const mapContainerStyle = {
+ height: '100%',
+};
+
+const defaultCenter = {
+ lat: 38.685,
+ lng: -115.234,
+};
+
+const mapOptions = {
+ fullscreenControl: false,
+}
+
+const drawingManager = new 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,
+ ],
+ },
+});
+
+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 default class CollectionMapView extends CollectionSubView<MapSchema, Partial<GoogleMapProps>>(MapSchema) {
+ private _dropDisposer?: DragManager.DragDropDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+
+
+ @observable private _map = null as unknown as google.maps.Map;
+ @observable private selectedPlace: LocationData | undefined;
+ @observable private markerMap: { [id: string]: google.maps.Marker } = {};
+ @observable private center = defaultCenter;
+ @observable private zoom = 2.5;
+ @observable private infoWindowOpen = false;
+ @observable private bounds = new window.google.maps.LatLngBounds();
+ @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 myPlaces: LocationData[] = [
+ { id: "id1", pos: { lat: 39.09366509575983, lng: -94.58751660204751 } },
+ { id: "id2", pos: { lat: 41.82399, lng: -71.41283 } },
+ { id: "id3", pos: { lat: 47.606214, lng: -122.33207 } },
+ ];
+
+ @action
+ private setSearchBox = (searchBox: any) => {
+ this.searchBox = searchBox;
+ }
+
+ // iterate myPlaces to size, center, and zoom map to contain all markers
+ private fitBounds = (map: google.maps.Map) => {
+ console.log('map bound is:' + this.bounds);
+ this.myPlaces ? this.myPlaces.map(place => {
+ this.bounds.extend(place.pos!);
+ }) : null;
+ map.fitBounds(this.bounds);
+ }
+
+ // store a reference to google map instance; fit map bounds to contain all markers
+ @action
+ private loadHandler = (map: google.maps.Map) => {
+ this._map = map;
+ drawingManager.setMap(map);
+ this.fitBounds(map);
+ }
+
+ @action
+ private markerClickHandler = (e: MouseEvent, place: any) => {
+ // set which place was clicked
+ this.selectedPlace = place;
+
+ console.log(this.selectedPlace);
+
+ // used so clicking a second marker works
+ if (this.infoWindowOpen) {
+ this.infoWindowOpen = false;
+ console.log("closeinfowindow")
+ }
+ this.infoWindowOpen = true;
+ console.log("open infowindow")
+ }
+
+ @action
+ private handleInfoWindowClose = () => {
+ if (this.infoWindowOpen) {
+ this.infoWindowOpen = false;
+ }
+ this.infoWindowOpen = false;
+ this.selectedPlace = undefined;
+ }
+
+ @action
+ private handlePlaceChanged = () => {
+ console.log(this.searchBox);
+ 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) {
+ console.log(this._map);
+ this._map.fitBounds(place.geometry.viewport);
+ } else {
+ console.log(this._map);
+ 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,
+ })
+ )
+ }
+
+
+ @action
+ private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => {
+ new window.google.maps.Marker({
+ position: location,
+ map: map
+ });
+ }
+
+ @action
+ private markerLoadHandler = (marker: google.maps.Marker, place: LocationData) => {
+ place.id ? this.markerMap[place.id] = marker : null;
+ }
+
+ 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)}
+ options={mapOptions}
+ >
+ <Autocomplete
+ onLoad={this.setSearchBox}
+ onPlaceChanged={this.handlePlaceChanged}>
+ <input ref={this.inputRef} className="searchbox" type="text" placeholder="Search anywhere:" />
+ </Autocomplete>
+
+ {this.myPlaces ? this.myPlaces.map(place =>
+ <Marker
+ position={place.pos}
+ onLoad={marker => this.markerLoadHandler(marker, place)}
+ onClick={e => this.markerClickHandler(e, place)}
+ draggable={false}
+ />
+ ) : null}
+ {this.infoWindowOpen && this.selectedPlace && (
+ <InfoWindow
+ anchor={this.markerMap[this.selectedPlace.id!]}
+ onCloseClick={this.handleInfoWindowClose}
+ >
+ <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
+ <div style={{ fontSize: 16 }}>
+ <div>
+ <img src="http://placekitten.com/200/300" />
+ <hr />
+ <form>
+ <label>Title: </label><br />
+ <input type="text" id="fname" name="fname"></input><br />
+ <label>Desription: </label><br />
+ <textarea style={{ height: 150 }} id="lname" name="lname" placeholder="Notes, a short description of this location, a brief comment, etc."></textarea>
+ </form>
+ <hr />
+ <div>
+ <button>New link+</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </InfoWindow>
+ )}
+ </GoogleMap>
+ </div>
+ {/* </LoadScript > */}
+ </div >
+ </div >;
+ }
+
+} \ No newline at end of file
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index a963f1cb9..dddccd18c 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -64,7 +64,6 @@ input.lm_title {
right: 10;
bottom: 10;
border: solid 1px;
- box-shadow: black 0.4vw 0.4vw 0.8vw;
width: 100%;
height: 100%;
transition: all 0.5s;
@@ -91,8 +90,8 @@ input.lm_title {
border-radius: 30px;
padding: 2px;
- >svg {
+ > svg {
margin-top: 3px;
transform: translate(0px, 7px);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 623e0f58d..7b77e859d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -429,7 +429,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
case StyleProp.PointerEvents: return "none";
case StyleProp.DocContents:
const background = doc.type === DocumentType.PDF ? "red" : doc.type === DocumentType.IMG ? "blue" : doc.type === DocumentType.RTF ? "orange" :
- doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : "gray";
+ doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : doc.type === DocumentType.MAP ? "blue" : "gray";
return doc.type === DocumentType.COL ?
undefined :
<div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;