Skip to content

Commit 4bd30f0

Browse files
committed
Initial version of Fable.ReactGoogleMaps
1 parent f6b37b8 commit 4bd30f0

File tree

6 files changed

+288
-0
lines changed

6 files changed

+288
-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: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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" "./js/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+
// export function getPosition (options) {
157+
// return new Promise(function (resolve, reject) {
158+
// navigator.geolocation.getCurrentPosition(resolve, reject, options);
159+
// });
160+
// }
161+
162+
// let getPosition (options: obj) : JS.Promise<obj> = importMember "./js/location.js"
163+
164+
// open Fable.PowerPack
165+
166+
// let getGeoPosition () =
167+
// promise {
168+
// let! pos = getPosition()
169+
// let c = pos?coords
170+
// return Coordinates.newPos (c?latitude |> unbox) (c?longitude |> unbox)
171+
// }
172+
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" 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: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { withScriptjs, withGoogleMap, GoogleMap, TrafficLayer, Marker } from "react-google-maps";
2+
const { SearchBox } = require("react-google-maps/lib/components/places/SearchBox");
3+
import React, {Component, Children} from 'react';
4+
5+
const TrafficMapComponent = withScriptjs(withGoogleMap((props) =>
6+
<GoogleMap
7+
defaultZoom={props.defaultZoom}
8+
onZoomChanged={props.onZoomChanged}
9+
onIdle={props.onIdle}
10+
defaultCenter={props.defaultCenter}
11+
center={props.center}
12+
ref={props.onMapMounted} >
13+
{props.showSearchBox && <SearchBox
14+
ref={props.onSearchBoxMounted}
15+
bounds={props.bounds}
16+
controlPosition={google.maps.ControlPosition.TOP_LEFT}
17+
onPlacesChanged={props.onPlacesChanged}
18+
>
19+
<input
20+
type="text"
21+
placeholder={props.searchBoxText}
22+
style={{
23+
boxSizing: `border-box`,
24+
border: `1px solid transparent`,
25+
width: `240px`,
26+
height: `30px`,
27+
marginTop: `10px`,
28+
padding: `0 12px`,
29+
borderRadius: `3px`,
30+
boxShadow: `0 1px 6px rgba(0, 0, 0, 0.3)`,
31+
fontSize: `14px`,
32+
outline: `none`,
33+
textOverflow: `ellipses`,
34+
}}
35+
/>
36+
</SearchBox>}
37+
{props.showTrafficLayer && <TrafficLayer autoUpdate /> }
38+
{props.markers}
39+
</GoogleMap>
40+
));
41+
42+
export class GoogleMapComponent extends React.PureComponent {
43+
refs = {}
44+
searchBoxRef = {}
45+
state = {
46+
isMarkerShown: false,
47+
}
48+
49+
componentDidMount() {
50+
this.delayedShowMarker()
51+
}
52+
53+
delayedShowMarker = () => {
54+
setTimeout(() => {
55+
this.setState({ isMarkerShown: true })
56+
}, 3000)
57+
}
58+
59+
render() {
60+
return (
61+
<TrafficMapComponent
62+
key="map"
63+
isMarkerShown={this.state.isMarkerShown}
64+
defaultZoom={this.props.defaultZoom}
65+
onZoomChanged={this.props.onZoomChanged}
66+
defaultCenter={this.props.defaultCenter}
67+
searchBoxText={this.props.searchBoxText}
68+
showSearchBox={this.props.showSearchBox}
69+
showTrafficLayer={this.props.showTrafficLayer}
70+
center={this.props.center}
71+
onPlacesChanged={() => {
72+
this.props.onPlacesChanged(this.searchBoxRef.getPlaces())
73+
}}
74+
onIdle={this.props.onIdle}
75+
onMapMounted={this.props.setRef}
76+
onSearchBoxMounted={ref => {
77+
this.searchBoxRef = ref;
78+
}}
79+
markers={this.props.markers}
80+
onMarkerClick={() => {
81+
this.setState({ isMarkerShown: false })
82+
this.delayedShowMarker()
83+
}}
84+
googleMapURL={"https://maps.googleapis.com/maps/api/js?key=" + this.props.apiKey + "&v=3.exp&libraries=geometry,drawing,places"}
85+
loadingElement={<div className={this.props.mapLoadingContainer} style={{ width: `100%` }} >Loading</div>}
86+
containerElement={<div className={this.props.mapContainer} style={{ width: `100%` }} /> }
87+
mapElement={<div style={{ height: `100%` }} />}
88+
/>
89+
)
90+
}
91+
}
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)