Skip to content

Commit df07fd8

Browse files
authored
Merge pull request #99 from fable-compiler/googlemaps
Initial version of Fable.ReactGoogleMaps
2 parents f6b37b8 + 53f2dc8 commit df07fd8

File tree

7 files changed

+303
-0
lines changed

7 files changed

+303
-0
lines changed

build.fsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ let gitOwner = "fable-compiler"
1919
let packages =
2020
[ "src/Fable.React/Fable.React.fsproj"
2121
"src/Fable.ReactLeaflet/Fable.ReactLeaflet.fsproj"
22+
"src/Fable.ReactGoogleMaps/Fable.ReactGoogleMaps.fsproj"
2223
"src/Fable.Recharts/Fable.Recharts.fsproj"
2324
]
2425

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
module Fable.Helpers.ReactGoogleMaps
2+
3+
open Fable.Core
4+
open Fable.Import
5+
open Fable.Core.JsInterop
6+
7+
module Coordinates =
8+
9+
type [<Pojo>] Position = {
10+
lat: float
11+
lng: float
12+
}
13+
14+
let newPos lat lng =
15+
{ lat = lat
16+
lng = lng }
17+
18+
type [<Pojo>] Bounds = {
19+
NE : Position
20+
SW : Position
21+
}
22+
23+
24+
module Places =
25+
26+
type [<Pojo>] Geometry = {
27+
location: Coordinates.Position
28+
}
29+
30+
type [<Pojo>] Place = {
31+
geometry: Geometry
32+
}
33+
34+
35+
module R = Fable.Helpers.React
36+
37+
type RCom = React.ComponentClass<obj>
38+
39+
type MapRef(mapRef) =
40+
41+
/// Get the current bounds of the Map
42+
member __.GetBounds() : Coordinates.Bounds =
43+
let bounds = mapRef?getBounds()
44+
let ne = bounds?getNorthEast()
45+
let sw = bounds?getSouthWest()
46+
47+
{ NE = Coordinates.newPos (ne?lat() |> unbox) (ne?lng() |> unbox)
48+
SW = Coordinates.newPos (sw?lat() |> unbox) (sw?lng() |> unbox) }
49+
50+
member __.GetZoom() : int =
51+
mapRef?getZoom() |> unbox
52+
53+
member __.GetCenter() : Coordinates.Position =
54+
mapRef?getCenter() |> unbox
55+
56+
module Props =
57+
58+
59+
type IInfoWindowProperties =
60+
interface end
61+
62+
[<RequireQualifiedAccess>]
63+
type InfoWindowProperties =
64+
| OnCloseClick of (unit -> unit)
65+
interface IInfoWindowProperties
66+
67+
type ISearchBoxProperties =
68+
interface end
69+
70+
[<RequireQualifiedAccess>]
71+
type SearchBoxProperties =
72+
| Ref of (obj -> unit)
73+
| OnPlacesChanged of (unit -> unit)
74+
interface ISearchBoxProperties
75+
76+
type IMarkerProperties =
77+
interface end
78+
79+
[<RequireQualifiedAccess>]
80+
type MarkerProperties =
81+
| Key of obj
82+
| Title of string
83+
| Icon of string
84+
| OnClick of (unit -> unit)
85+
| Position of Coordinates.Position
86+
interface IMarkerProperties
87+
88+
type IMarkerClustererProperties =
89+
interface end
90+
91+
[<RequireQualifiedAccess>]
92+
type MarkerClustererProperties =
93+
| Key of obj
94+
| AverageCenter of bool
95+
| MaxZoom of int
96+
| EnableRetinaIcons of bool
97+
| GridSize of float
98+
interface IMarkerClustererProperties
99+
100+
type IMapProperties =
101+
interface end
102+
103+
[<RequireQualifiedAccess>]
104+
type MapProperties =
105+
| SetRef of (obj -> unit)
106+
| ApiKey of string
107+
| DefaultZoom of int
108+
| SearchBoxText of string
109+
| ShowSearchBox of bool
110+
| ShowTrafficLayer of bool
111+
| DefaultCenter of Coordinates.Position
112+
| Center of Coordinates.Position
113+
| OnCenterChanged of (unit -> unit)
114+
| OnPlacesChanged of (Places.Place [] -> unit)
115+
| OnZoomChanged of (unit -> unit)
116+
| OnIdle of (unit -> unit)
117+
| Markers of React.ReactElement
118+
| MapLoadingContainer of string
119+
| MapContainer of string
120+
interface IMapProperties
121+
let InfoWindow: RCom = import "InfoWindow" "react-google-maps"
122+
123+
/// A wrapper around google.maps.InfoWindow
124+
let infoWindow (props:Props.IInfoWindowProperties list) child : React.ReactElement =
125+
R.from InfoWindow (keyValueList CaseRules.LowerFirst props) [child]
126+
127+
let SearchBox: RCom = import "SearchBox" "react-google-maps"
128+
129+
/// A wrapper around google.maps.places.SearchBox on the map
130+
let searchBox (props:Props.ISearchBoxProperties list) children : React.ReactElement =
131+
R.from SearchBox (keyValueList CaseRules.LowerFirst props) children
132+
133+
134+
let Marker: RCom = import "Marker" "react-google-maps"
135+
136+
/// A wrapper around google.maps.Marker
137+
let marker (props:Props.IMarkerProperties list) children : React.ReactElement =
138+
R.from Marker (keyValueList CaseRules.LowerFirst props) children
139+
140+
let MarkerClusterer: RCom = import "MarkerClusterer" "react-google-maps/lib/components/addons/MarkerClusterer.js"
141+
142+
/// A wrapper around MarkerClusterer - https://github.com/mahnunchik/markerclustererplus/blob/master/docs/reference.html
143+
let markerClusterer (props:Props.IMarkerClustererProperties list) (markers:React.ReactElement list) : React.ReactElement =
144+
R.from MarkerClusterer (keyValueList CaseRules.LowerFirst props) markers
145+
146+
let GoogleMapComponent: RCom = import "GoogleMapComponent" "./mapComponent.js"
147+
148+
149+
/// A wrapper around google.maps.Map
150+
let googleMap (props:Props.IMapProperties list) : React.ReactElement =
151+
152+
R.from GoogleMapComponent (keyValueList CaseRules.LowerFirst props) []
153+
154+
155+
156+
let getPosition (options: obj) : JS.Promise<obj> = importMember "./location.js"
157+
158+
open Fable.PowerPack
159+
160+
let getGeoPosition () =
161+
promise {
162+
let! pos = getPosition()
163+
let c = pos?coords
164+
return Coordinates.newPos (c?latitude |> unbox) (c?longitude |> unbox)
165+
}
166+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<PropertyGroup>
4+
<Version>0.2.0</Version>
5+
<TargetFramework>netstandard1.6</TargetFramework>
6+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<ProjectReference Include="../Fable.React/Fable.React.fsproj" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<Compile Include="Fable.Helpers.ReactGoogleMaps.fs" />
13+
</ItemGroup>
14+
<ItemGroup>
15+
<Content Include="*.fsproj; *.fs; *.js" PackagePath="fable\" />
16+
</ItemGroup>
17+
<Import Project="..\..\.paket\Paket.Restore.targets" />
18+
</Project>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### 0.1.0-beta-001
2+
3+
* Initial release
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function getPosition (options) {
2+
return new Promise(function (resolve, reject) {
3+
navigator.geolocation.getCurrentPosition(resolve, reject, options);
4+
});
5+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { withScriptjs, withGoogleMap, GoogleMap, TrafficLayer } from "react-google-maps";
2+
const { SearchBox } = require("react-google-maps/lib/components/places/SearchBox");
3+
import React from 'react';
4+
5+
const TrafficMapComponent = withScriptjs(withGoogleMap((props) => {
6+
var childs = [ props.markers ];
7+
if(props.showSearchBox) {
8+
var inputBox =
9+
React.createElement("input",
10+
{ type:"text",
11+
placeholder : props.searchBoxText,
12+
style : {
13+
boxSizing: `border-box`,
14+
border: `1px solid transparent`,
15+
width: `240px`,
16+
height: `30px`,
17+
marginTop: `10px`,
18+
padding: `0 12px`,
19+
borderRadius: `3px`,
20+
boxShadow: `0 1px 6px rgba(0, 0, 0, 0.3)`,
21+
fontSize: `14px`,
22+
outline: `none`,
23+
textOverflow: `ellipses`}
24+
});
25+
26+
var searchBox =
27+
React.createElement(SearchBox,
28+
{ ref : props.onSearchBoxMounted,
29+
bounds : props.bounds,
30+
controlPosition : google.maps.ControlPosition.TOP_LEFT,
31+
onPlacesChanged : props.onPlacesChanged },
32+
inputBox
33+
);
34+
35+
childs = [ searchBox, ...childs ];
36+
};
37+
38+
if(props.showTrafficLayer) {
39+
var traffic = React.createElement(TrafficLayer, { });
40+
childs = [ traffic, ...childs ]
41+
};
42+
43+
return (
44+
React.createElement(
45+
GoogleMap,
46+
{ defaultZoom : props.defaultZoom,
47+
onZoomChanged : props.onZoomChanged,
48+
onIdle : props.onIdle,
49+
defaultCenter : props.defaultCenter,
50+
center : props.center,
51+
ref : props.onMapMounted },
52+
childs))
53+
}));
54+
55+
export class GoogleMapComponent extends React.PureComponent {
56+
refs = {}
57+
searchBoxRef = {}
58+
state = {
59+
isMarkerShown: false,
60+
}
61+
62+
componentDidMount() {
63+
this.delayedShowMarker()
64+
}
65+
66+
delayedShowMarker = () => {
67+
setTimeout(() => {
68+
this.setState({ isMarkerShown: true })
69+
}, 3000)
70+
}
71+
72+
render() {
73+
var loading = React.createElement("div", { className : this.props.mapLoadingContainer, style : { width: `100%` }}, "Loading");
74+
var container = React.createElement("div", { className : this.props.mapContainer});
75+
var mapElement = React.createElement("div", { style : { height: `100%` }});
76+
return (
77+
React.createElement(
78+
TrafficMapComponent,
79+
{ key:"map",
80+
isMarkerShown: this.state.isMarkerShown,
81+
defaultZoom: this.props.defaultZoom,
82+
onZoomChanged: this.props.onZoomChanged,
83+
defaultCenter: this.props.defaultCenter,
84+
searchBoxText: this.props.searchBoxText,
85+
showSearchBox: this.props.showSearchBox,
86+
showTrafficLayer: this.props.showTrafficLayer,
87+
center: this.props.center,
88+
onPlacesChanged: () => {
89+
this.props.onPlacesChanged(this.searchBoxRef.getPlaces())
90+
},
91+
onIdle: this.props.onIdle,
92+
onMapMounted: this.props.setRef,
93+
onSearchBoxMounted: ref => {
94+
this.searchBoxRef = ref;
95+
},
96+
markers: this.props.markers,
97+
onMarkerClick: () => {
98+
this.setState({ isMarkerShown: false })
99+
this.delayedShowMarker()
100+
},
101+
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=" + this.props.apiKey + "&v=3.exp&libraries=geometry,drawing,places",
102+
loadingElement: loading,
103+
containerElement: container,
104+
mapElement: mapElement
105+
}))
106+
}
107+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FSharp.Core
2+
Fable.Core
3+
Fable.Import.Browser

0 commit comments

Comments
 (0)