├── .gitignore
├── src
├── index.js
└── GMap
│ ├── marker.tmpl.js
│ ├── GoogleMapsApi.js
│ ├── index.js
│ └── stylers.js
├── gulpfile.json
├── package.json
├── gulpfile.js
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Misc
2 | /tmp
3 | .DS_Store
4 | node_modules
5 | .log
6 | .remote-sync.json
7 | .sass-cache
8 | vendor
9 | dist
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as GMap from './GMap'
2 |
3 | /**
4 | * Init Map on 'js-map'
5 | * Add API Key
6 | */
7 | if (document.querySelector('.js-map')) {
8 | GMap.GMap('.js-map', 'YOUR_GOOGLE_MAPS_API_KEY_HERE')
9 | }
10 |
--------------------------------------------------------------------------------
/gulpfile.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "GoogleMapES6",
3 | "version": "1.0.0",
4 | "description": "ES6 style google maps ",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Stephen Scaff",
10 | "license": "DWTFYL"
11 | }
12 |
--------------------------------------------------------------------------------
/src/GMap/marker.tmpl.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Marker Template
4 | * @param {obj} data - property data/json
5 | */
6 | function markerTmpl(data) {
7 |
8 | const url = encodeURIComponent(data.address)
9 |
10 | return `
11 |
12 |
13 |
14 |
${data.title}
15 |
16 | ${data.address}
17 |
18 |
Get Directions
19 |
20 |
21 |
22 | `;
23 | }
24 |
25 | export default markerTmpl;
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "es6googlemaps",
3 | "version": "1.0.0",
4 | "description": "Google maps api, as es6 style modules.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/stephenscaff/google-maps-es6"
12 | },
13 | "keywords": [
14 | "Google Maps",
15 | "google maps api",
16 | "es6 google maps",
17 | "maps"
18 | ],
19 | "author": "Stephen Scaff",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/stephenscaff/google-maps-es6#issues"
23 | },
24 | "homepage": "https://github.com/stephenscaff/google-maps-es6#readme",
25 | "dependencies": {},
26 | "devDependencies": {
27 | "@babel/core": "^7.8.4",
28 | "@babel/preset-env": "^7.8.4",
29 | "babelify": "^10.0.0",
30 | "browserify": "^16.5.0",
31 | "gulp": "^4.0.2",
32 | "gulp-rename": "^2.0.0",
33 | "gulp-terser": "^1.2.0",
34 | "vinyl-buffer": "^1.0.1",
35 | "vinyl-source-stream": "^2.0.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 |
2 | const gulp = require('gulp'),
3 | babelify = require('babelify'),
4 | browserify = require('browserify'),
5 | buffer = require('vinyl-buffer'),
6 | rename = require('gulp-rename'),
7 | source = require('vinyl-source-stream'),
8 | terser = require('gulp-terser');
9 |
10 |
11 | // Error handler
12 | function handleError(err) {
13 | console.log(err.toString())
14 | this.emit('end')
15 | }
16 |
17 | function buildJS() {
18 | const bundler = browserify('src/index.js').transform(
19 | 'babelify',
20 | { presets: ['@babel/preset-env'] }
21 | )
22 | return bundler.bundle()
23 | .on('error', handleError)
24 | .pipe(source('src/index.js'))
25 | .pipe(buffer())
26 | .pipe(terser({
27 | mangle: false,
28 | compress: true
29 | }))
30 | .pipe(rename("es6-gmap.js"))
31 | .pipe(gulp.dest('dist/'))
32 | }
33 |
34 |
35 | function watch() {
36 | gulp.watch('src/**/*', buildJS)
37 | }
38 |
39 | var build = gulp.parallel(
40 | buildJS,
41 | watch
42 | )
43 |
44 | gulp.task(build)
45 | gulp.task('default', build)
46 |
--------------------------------------------------------------------------------
/src/GMap/GoogleMapsApi.js:
--------------------------------------------------------------------------------
1 | /**
2 | * GoogleMapsApi
3 | * Class to load google maps api with api key
4 | * and global Callback to init map after resolution of promise.
5 | *
6 | * @exports {GoogleMapsApi}
7 | * @example MapApi = new GoogleMapsApi();
8 | * MapApi.load().then(() => {});
9 | */
10 | class GoogleMapsApi {
11 |
12 | /**
13 | * Constructor
14 | * @property {string} apiKey
15 | * @property {string} callbackName
16 | */
17 | constructor(gApiKey) {
18 |
19 | // api key for google maps
20 | this.apiKey = gApiKey;
21 |
22 | // Set global callback
23 | if (!window._GoogleMapsApi) {
24 | this.callbackName = '_GoogleMapsApi.mapLoaded';
25 | window._GoogleMapsApi = this;
26 | window._GoogleMapsApi.mapLoaded = this.mapLoaded.bind(this);
27 | }
28 | }
29 |
30 | /**
31 | * Load
32 | * Create script element with google maps
33 | * api url, containing api key and callback for
34 | * map init.
35 | * @return {promise}
36 | * @this {_GoogleMapsApi}
37 | */
38 | load() {
39 | if (!this.promise) {
40 | this.promise = new Promise(resolve => {
41 | this.resolve = resolve;
42 |
43 | if (typeof window.google === 'undefined') {
44 | const script = document.createElement('script');
45 | script.src = `//maps.googleapis.com/maps/api/js?key=${this.apiKey}&callback=${this.callbackName}`;
46 | script.async = true;
47 | document.body.append(script);
48 |
49 | } else {
50 | this.resolve();
51 | }
52 | });
53 | }
54 |
55 | return this.promise;
56 | }
57 |
58 | /**
59 | * mapLoaded
60 | * Global callback for loaded/resolved map instance.
61 | * @this {_GoogleMapsApi}
62 | *
63 | */
64 | mapLoaded() {
65 |
66 | if (this.resolve) {
67 | this.resolve();
68 | }
69 | }
70 | }
71 |
72 | export default GoogleMapsApi;
73 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Google Maps, ES6 Like
2 |
3 | Example for working with Google Maps API, all es6 like.
4 |
5 | Contains a loader class to inject the Google Maps script, with a promise that upon resolution provides a callback for a map rendering function.
6 |
7 | Also includes a separate custom marker template file and stylers object.
8 |
9 | The js structure is as follows:
10 |
11 | ```
12 | -js
13 | |- GMap
14 | |- GoogleMapsApi.js - google maps api class
15 | |- marker.tmpl.js - custom marker template
16 | |- stylers.js - JSON styles and custom icon
17 | |- index.js - functions to render map, marker and infowindow
18 | |- index.js - import and init example
19 | ```
20 |
21 | ## Install and Build
22 |
23 | To build this project first install
24 |
25 | ```
26 | npm install
27 | ```
28 |
29 | Then build it like
30 |
31 | ```
32 | gulp
33 | ```
34 |
35 | Or, just add `src/GMap` to your existing es6 project.
36 |
37 |
38 | ## Usage
39 |
40 | Import map components then pass your Google Map selector and Google Maps api key to `Gmap($mapEl, $apiKey)`
41 | An example init is at `src/index.js`:
42 |
43 |
44 | ```
45 | import * as Gmap from './GMap'
46 |
47 | Gmap.GMap('.js-map', 'YOUR_GOOGLE_MAPS_API_KEY_HERE')
48 | ```
49 |
50 | ## Markup
51 |
52 | The map div uses uses data attributes to pass values to the map instance.
53 | The required are `data-lat` and `data-lng`. For example:
54 |
55 | ```
56 |
68 | ```
69 |
70 | ## Styles
71 | Your map won't render unless it has a height defined. Without example above, that could look Like:
72 |
73 | ```
74 | .map__wrap{
75 | position: relative;
76 | width: 100%;
77 | }
78 |
79 | .map__map {
80 | width: 100%;
81 | height: 100%;
82 | min-height: 50em;
83 | }
84 | ```
85 |
86 | Odds are you already know that.
87 |
88 | ## Info Window Template
89 | Edit the info window popup template at `src/Gmap/marker.tmpl.js`
90 |
91 |
92 | ## Google Maps Styles
93 | Edit Google Map's styles JSON at `src/GMap/stylers.js`
94 |
95 |
96 | Have fun.
97 |
--------------------------------------------------------------------------------
/src/GMap/index.js:
--------------------------------------------------------------------------------
1 | import GoogleMapsApi from './GoogleMapsApi'
2 | import { stylers } from './stylers'
3 | import markerTmpl from './marker.tmpl'
4 |
5 | /**
6 | * Location Map
7 | * Main map rendering function that uses our GMaps API class
8 | * @param {string} el - Google Map selector
9 | */
10 | export function GMap(el, apiKey) {
11 |
12 | const gApiKey = apiKey
13 | console.log(gApiKey)
14 | const gmapApi = new GoogleMapsApi(gApiKey)
15 | const mapEl = document.querySelector(el)
16 | const data = {
17 | lat: parseFloat(mapEl.dataset.lat ? mapEl.dataset.lat : 0),
18 | lng: parseFloat(mapEl.dataset.lng ? mapEl.dataset.lng : 0),
19 | address: mapEl.dataset.address,
20 | title: mapEl.dataset.title ? mapEl.dataset.title: "Map",
21 | zoom: parseFloat(mapEl.dataset.zoom ? mapEl.dataset.zoom: 12),
22 | }
23 | // Call map renderer
24 | gmapApi.load().then(() => {
25 | renderMap(mapEl, data)
26 | })
27 | }
28 |
29 | /**
30 | * Render Map
31 | * @param {map obj} mapEl - Google Map
32 | * @param {obj} data - map data
33 | */
34 | function renderMap(mapEl, data) {
35 |
36 | const options = {
37 | mapTypeId: google.maps.MapTypeId.ROADMAP,
38 | styles: stylers.styles,
39 | zoom: data.zoom,
40 | center: {
41 | lat: data.lat,
42 | lng: data.lng
43 | }
44 | }
45 |
46 | const map = new google.maps.Map(mapEl, options)
47 |
48 | renderMarker(map, data)
49 | }
50 |
51 | /**
52 | * Render Marker
53 | * Renders custom map marker and infowindow
54 | * @param {map element} mapEl
55 | * @param {object} data
56 | */
57 | function renderMarker(map, data) {
58 |
59 | const icon = {
60 | url: stylers.icons.red,
61 | scaledSize: new google.maps.Size(80, 80)
62 | }
63 |
64 | const tmpl = markerTmpl(data)
65 |
66 | const marker = new google.maps.Marker({
67 | position: new google.maps.LatLng(data.lat, data.lng),
68 | map: map,
69 | icon: icon,
70 | title: data.title,
71 | content: tmpl,
72 | animation: google.maps.Animation.DROP
73 | })
74 |
75 | const infowindow = new google.maps.InfoWindow()
76 |
77 | handleMarkerClick(map, marker, infowindow)
78 | }
79 |
80 | /**
81 | * Handle Marker Click
82 | *
83 | * @param {map obj} mapEl
84 | * @param {marker} marker
85 | * @param {infowindow} infoWindow
86 | */
87 | function handleMarkerClick(map, marker, infowindow) {
88 |
89 | google.maps.event.addListener(marker, 'click', function() {
90 | infowindow.setContent(marker.content)
91 | infowindow.open(map, marker)
92 | })
93 |
94 | google.maps.event.addListener(map, 'click', function(event) {
95 | if (infowindow) {
96 | infowindow.close(map, infowindow)
97 | }
98 | })
99 | }
100 |
--------------------------------------------------------------------------------
/src/GMap/stylers.js:
--------------------------------------------------------------------------------
1 | export const stylers = {
2 |
3 | /**
4 | * Map Styler JSON
5 | */
6 | styles: [
7 | {
8 | "featureType": "administrative",
9 | "elementType": "labels.text.fill",
10 | "stylers": [ {
11 | "color": "#444444"
12 | } ]
13 | }, {
14 | "featureType": "landscape",
15 | "elementType": "all",
16 | "stylers": [ {
17 | "color": "#f2f2f2"
18 | } ]
19 | }, {
20 | "featureType": "landscape.natural.landcover",
21 | "elementType": "labels.icon",
22 | "stylers": [ {
23 | "visibility": "simplified"
24 | } ]
25 | }, {
26 | "featureType": "poi",
27 | "elementType": "all",
28 | "stylers": [ {
29 | "visibility": "off"
30 | } ]
31 | }, {
32 | "featureType": "road",
33 | "elementType": "all",
34 | "stylers": [ {
35 | "saturation": -100
36 | }, {
37 | "lightness": 45
38 | } ]
39 | }, {
40 | "featureType": "road.highway",
41 | "elementType": "all",
42 | "stylers": [ {
43 | "visibility": "simplified"
44 | } ]
45 | }, {
46 | "featureType": "road.highway",
47 | "elementType": "geometry.fill",
48 | "stylers": [ {
49 | "color": "#ffffff"
50 | } ]
51 | }, {
52 | "featureType": "road.arterial",
53 | "elementType": "labels.icon",
54 | "stylers": [ {
55 | "visibility": "off"
56 | } ]
57 | }, {
58 | "featureType": "transit",
59 | "elementType": "all",
60 | "stylers": [ {
61 | "visibility": "off"
62 | } ]
63 | }, {
64 | "featureType": "water",
65 | "elementType": "all",
66 | "stylers": [ {
67 | "color": "#dde6e8"
68 | }, {
69 | "visibility": "on"
70 | } ]
71 | }
72 | ],
73 |
74 | /**
75 | * Map Icon/Pin SVG
76 | * SVG is base64 encoded.
77 | */
78 | icons: {
79 | red: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMzVweCIgaGVpZ2h0PSI0M3B4IiB2aWV3Qm94PSIwIDAgMzUgNDMiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUxLjMgKDU3NTQ0KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5QaW4gLSBPUiBDb3B5IDM8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz4KICAgICAgICA8cGF0aCBkPSJNNjgyLDM3MSBMNjgyLDM4MCBMNjczLDM3MSBMNjU5LDM3MSBMNjU5LDM0OCBMNjgyLDM0OCBMNjgyLDM3MSBaIiBpZD0icGF0aC0xIj48L3BhdGg+CiAgICAgICAgPGZpbHRlciB4PSItNDMuNSUiIHk9Ii0yNS4wJSIgd2lkdGg9IjE4Ny4wJSIgaGVpZ2h0PSIxNjIuNSUiIGZpbHRlclVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgaWQ9ImZpbHRlci0yIj4KICAgICAgICAgICAgPGZlT2Zmc2V0IGR4PSIwIiBkeT0iMiIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9InNoYWRvd09mZnNldE91dGVyMSI+PC9mZU9mZnNldD4KICAgICAgICAgICAgPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMyIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIj48L2ZlR2F1c3NpYW5CbHVyPgogICAgICAgICAgICA8ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMC4yMTE3NjQ3MDYgICAwIDAgMCAwIDAuMjA3ODQzMTM3ICAgMCAwIDAgMCAwLjIxNTY4NjI3NSAgMCAwIDAgMC4yNCAwIiB0eXBlPSJtYXRyaXgiIGluPSJzaGFkb3dCbHVyT3V0ZXIxIj48L2ZlQ29sb3JNYXRyaXg+CiAgICAgICAgPC9maWx0ZXI+CiAgICA8L2RlZnM+CiAgICA8ZyBpZD0i8J+OqC1EZXNpZ24iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJQcm9mZXNzaW9uYWwtRGV0YWlsLS0tQ3JpZGVyYS0iIHRyYW5zZm9ybT0idHJhbnNsYXRlKC04OTAuMDAwMDAwLCAtNDE5MS4wMDAwMDApIiBmaWxsLXJ1bGU9Im5vbnplcm8iPgogICAgICAgICAgICA8ZyBpZD0iUHJvcGVydGllcy1NYXAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUwLjAwMDAwMCwgMzUzMy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJMb2NhdGlvbnMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE4Ny4wMDAwMDAsIDMxNC4wMDAwMDApIj4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iUGlucyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxnIGlkPSJQaW4tLS1PUi1Db3B5LTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHVzZSBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIxIiBmaWx0ZXI9InVybCgjZmlsdGVyLTIpIiB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx1c2UgZmlsbD0iI0UwNDQwMyIgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg=='
80 | }
81 | };
82 |
--------------------------------------------------------------------------------