mirror of
https://github.com/google-map-react/google-map-react.git
synced 2025-12-08 18:26:32 +00:00
Rewrite library using create-react-library (#900)
* Rewrite library using create-react-library * Export utils properly * Remove unnecessary code and fix eslintignore * Update CHANGELOG
This commit is contained in:
parent
e70d0f748d
commit
b3bc11c215
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@ -0,0 +1,6 @@
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
.snapshots/
|
||||
*.min.js
|
||||
*.spec.js
|
||||
68
.eslintrc
68
.eslintrc
@ -1,65 +1,33 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"prettier",
|
||||
"prettier/flowtype",
|
||||
"standard",
|
||||
"standard-react",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/standard",
|
||||
"prettier/react"
|
||||
],
|
||||
"plugins": [
|
||||
"babel",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true,
|
||||
"jest": true,
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2020,
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"generators": true,
|
||||
"experimentalObjectRestSpread": true
|
||||
"legacyDecorators": true,
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "16"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"no-bitwise": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/require-default-props": 0,
|
||||
"react/jsx-handler-names": 0,
|
||||
"react/jsx-fragments": 0,
|
||||
"react/no-unused-prop-types": 0,
|
||||
"no-sequences": 1,
|
||||
"comma-dangle": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-restricted-properties": 0,
|
||||
"jsx-a11y/no-static-element-interactions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"react/sort-comp": 0,
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"polyfills": [
|
||||
"fetch__"
|
||||
]
|
||||
},
|
||||
"globals": {}
|
||||
"import/export": 0
|
||||
}
|
||||
}
|
||||
35
.flowconfig
35
.flowconfig
@ -1,35 +0,0 @@
|
||||
[ignore]
|
||||
.*/coverage/.*
|
||||
.*/scripts/.*
|
||||
.*/node_modules/flow-bin/.*
|
||||
.*/node_modules/.*/flow-bin.*/.*
|
||||
.*/node_modules/.*/broken.json
|
||||
.*/node_modules/fixed-data-table/*
|
||||
.*/node_modules/.*/fixtures/package.json
|
||||
# .*/node_modules/.*/*.json
|
||||
.*/node_modules/react-motion.*/*.js
|
||||
.*/build/**/*.json
|
||||
|
||||
[libs]
|
||||
./flowtypes
|
||||
|
||||
[include]
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
esproposal.class_static_fields=enable
|
||||
esproposal.class_instance_fields=enable
|
||||
esproposal.decorators=ignore
|
||||
suppress_comment=.*\\$FlowIssue
|
||||
module.system.node.resolve_dirname=node_modules
|
||||
module.system.node.resolve_dirname=src
|
||||
module.system.node.resolve_dirname=app
|
||||
munge_underscores=true
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,4 +1,22 @@
|
||||
lib/*
|
||||
dist/*
|
||||
/node_modules
|
||||
.idea/
|
||||
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# builds
|
||||
build
|
||||
dist
|
||||
.rpt2_cache
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
src
|
||||
scripts
|
||||
__tests__
|
||||
examples
|
||||
.babelrc
|
||||
.eslintrc
|
||||
.github
|
||||
.vscode
|
||||
10
.prettierrc
Normal file
10
.prettierrc
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
- stable
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- node_modules
|
||||
before_script:
|
||||
- git diff --exit-code # make sure that yarn.lock didn't change
|
||||
script:
|
||||
- yarn run lint
|
||||
- yarn test:lint
|
||||
- yarn test
|
||||
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,4 +0,0 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
2
API.md
2
API.md
@ -197,7 +197,7 @@ render() {
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
import { fitBounds } from 'google-map-react/utils';
|
||||
import { fitBounds } from 'google-map-react';
|
||||
|
||||
const bounds = {
|
||||
nw: {
|
||||
|
||||
26
CHANGELOG.md
26
CHANGELOG.md
@ -1,11 +1,18 @@
|
||||
## [unreleased(major)]
|
||||
|
||||
### Changed
|
||||
|
||||
- Rewrite library using [create-react-library](https://www.npmjs.com/package/create-react-library)
|
||||
- **Breaking** Move `google-map-react/utils` to module import, so instead of doing `import { utilName } from 'google-map-react/utils` you will do `import { utilName } from google-map-react`
|
||||
|
||||
## 0.9v
|
||||
|
||||
Add prop `onDragEnd` to react on the `dragend` event
|
||||
|
||||
Add [google-map-clustering-example](https://github.com/istarkov/google-map-clustering-example)
|
||||
|
||||
Add prop `onTilesLoaded` to react on the `tilesloaded` event
|
||||
|
||||
###0.9v
|
||||
|
||||
Add: `bootstrapURLKeys` (object) instead of `apiKey` prop
|
||||
|
||||
(`apiKey` prop is now deprecated)
|
||||
@ -22,12 +29,11 @@ Example:
|
||||
>
|
||||
```
|
||||
|
||||
###0.8v
|
||||
## 0.8v
|
||||
|
||||
Draggable markers support, examples comig soon.
|
||||
|
||||
|
||||
###Mon Oct 5 2015
|
||||
## Mon Oct 5 2015
|
||||
|
||||
Add `OnChange({center, zoom, bounds: {nw, se}, size, ...oherMapProps})`
|
||||
|
||||
@ -58,10 +64,10 @@ Add `utils` functions, with `fitBounds` and other functions
|
||||
```
|
||||
|
||||
|
||||
###Sun Oct 4 2015
|
||||
## Sun Oct 4 2015
|
||||
Add minZoom calculation, to prevent situations when one map point can have multiple screen coordinates.
|
||||
|
||||
###Sun Oct 4 2015
|
||||
## Sun Oct 4 2015
|
||||
Add ability to access to internal google api
|
||||
|
||||
```javascript
|
||||
@ -71,14 +77,14 @@ Add ability to access to internal google api
|
||||
(*to prevent warn message add _yesIWantToUseGoogleMapApiInternals_ property to GoogleMap*)
|
||||
|
||||
|
||||
###Sun Oct 4 2015
|
||||
## Sun Oct 4 2015
|
||||
|
||||
Add: `defaultZoom` `defaultCenter` properties, it closes #9 #10
|
||||
|
||||
###Sat Oct 3 2015
|
||||
## Sat Oct 3 2015
|
||||
|
||||
Support `center` prop as `{lat, lng}` object
|
||||
|
||||
###Thu Oct 1 2015
|
||||
## Thu Oct 1 2015
|
||||
|
||||
Add `onClick`, `onZoomAnimationStart`, `onZoomAnimationEnd` events.
|
||||
|
||||
2
DOC.md
2
DOC.md
@ -144,7 +144,7 @@ _)
|
||||
### Helper utilities
|
||||
|
||||
```javascript
|
||||
import { meters2ScreenPixels } from 'google-map-react/utils';
|
||||
import { meters2ScreenPixels } from 'google-map-react';
|
||||
const { w, h } = meters2ScreenPixels(sizeInMeters, { lat, lng } /* marker coords*/, zoom /* map zoom*/);
|
||||
```
|
||||
|
||||
|
||||
31
bower.json
31
bower.json
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "google-map-react",
|
||||
"description": "isomorphic google map react component, allows render react components on the google map",
|
||||
"main": "lib/umd/GoogleMapReact.js",
|
||||
"homepage": "https://github.com/google-map-react/google-map-react",
|
||||
"authors": [
|
||||
"Ivan Starkov <istarkov@gmail.com>"
|
||||
],
|
||||
"keywords": [
|
||||
"react",
|
||||
"reactjs",
|
||||
"google",
|
||||
"map",
|
||||
"maps",
|
||||
"isomorphic",
|
||||
"render",
|
||||
"component",
|
||||
"javascript",
|
||||
"react-component"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"src",
|
||||
"scripts",
|
||||
"package.json",
|
||||
"webpack.config.js"
|
||||
]
|
||||
}
|
||||
109
develop/GMap.js
109
develop/GMap.js
@ -1,109 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
compose,
|
||||
defaultProps,
|
||||
withHandlers,
|
||||
withState,
|
||||
withContext,
|
||||
withProps,
|
||||
withPropsOnChange,
|
||||
} from 'recompose';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { susolvkaCoords, generateMarkers } from './data/fakeData';
|
||||
|
||||
import SimpleMarker from './markers/SimpleMarker';
|
||||
import GoogleMapReact from '../src';
|
||||
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import { GOOGLE_API_KEY } from './config/Google_API_key';
|
||||
|
||||
export const gMap = (
|
||||
{
|
||||
style,
|
||||
hoverDistance,
|
||||
options,
|
||||
mapParams: { center, zoom },
|
||||
onChange,
|
||||
onChildMouseEnter,
|
||||
onChildMouseLeave,
|
||||
markers,
|
||||
draggable, // hoveredMarkerId,
|
||||
}
|
||||
) => (
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: GOOGLE_API_KEY,
|
||||
}}
|
||||
style={style}
|
||||
options={options}
|
||||
draggable={draggable}
|
||||
hoverDistance={hoverDistance}
|
||||
zoom={zoom}
|
||||
center={center}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
>
|
||||
{markers}
|
||||
</GoogleMapReact>
|
||||
);
|
||||
|
||||
export const gMapHOC = compose(
|
||||
defaultProps({
|
||||
clusterRadius: 60,
|
||||
hoverDistance: 30,
|
||||
options: {
|
||||
minZoom: 3,
|
||||
maxZoom: 15,
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
flex: 1,
|
||||
},
|
||||
}),
|
||||
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
|
||||
// withState so you could change markers if you want
|
||||
withStateSelector('markers', 'setMarkers', () =>
|
||||
createSelector(
|
||||
({ route: { markersCount = 20 } }) => markersCount,
|
||||
markersCount => generateMarkers(markersCount)
|
||||
)),
|
||||
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
|
||||
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
|
||||
// describe events
|
||||
withHandlers({
|
||||
onChange: ({ setMapParams }) =>
|
||||
({ center, zoom, bounds }) => {
|
||||
setMapParams({ center, zoom, bounds });
|
||||
},
|
||||
onChildMouseEnter: ({ setHoveredMarkerId }) =>
|
||||
(hoverKey, { id }) => {
|
||||
setHoveredMarkerId(id);
|
||||
},
|
||||
onChildMouseLeave: ({ setHoveredMarkerId }) =>
|
||||
() => {
|
||||
setHoveredMarkerId(-1);
|
||||
},
|
||||
}),
|
||||
withPropsOnChange(['markers', 'mapParams'], ({
|
||||
markers,
|
||||
mapParams: { bounds },
|
||||
}) => ({
|
||||
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
|
||||
})),
|
||||
withProps(({ hoveredMarkerId }) => ({
|
||||
draggable: hoveredMarkerId === -1,
|
||||
})),
|
||||
withPropsOnChange(['markers'], ({ markers }) => ({
|
||||
markers: markers.map(({ ...markerProps, id }) => (
|
||||
<SimpleMarker key={id} id={id} {...markerProps} />
|
||||
)),
|
||||
}))
|
||||
);
|
||||
|
||||
export default gMapHOC(gMap);
|
||||
@ -1,129 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
compose,
|
||||
defaultProps,
|
||||
withHandlers,
|
||||
withState,
|
||||
withContext,
|
||||
withProps,
|
||||
withPropsOnChange,
|
||||
} from 'recompose';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import {
|
||||
susolvkaCoords,
|
||||
generateMarkers,
|
||||
heatmapData,
|
||||
generateHeatmapData,
|
||||
} from './data/fakeData';
|
||||
|
||||
import GoogleMapReact from '../src';
|
||||
import SimpleMarker from './markers/SimpleMarker';
|
||||
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import { GOOGLE_API_KEY } from './config/Google_API_key';
|
||||
import withSafeInterval from './utils/withSafeInterval';
|
||||
|
||||
export const gMapHeatmap = (
|
||||
{
|
||||
style,
|
||||
hoverDistance,
|
||||
options,
|
||||
heatmap,
|
||||
mapParams: { center, zoom },
|
||||
onChange,
|
||||
onChildMouseEnter,
|
||||
onChildMouseLeave,
|
||||
markers,
|
||||
draggable, // hoveredMarkerId,
|
||||
}
|
||||
) => (
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: GOOGLE_API_KEY,
|
||||
}}
|
||||
style={style}
|
||||
options={options}
|
||||
draggable={draggable}
|
||||
hoverDistance={hoverDistance}
|
||||
zoom={zoom}
|
||||
center={center}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
heatmap={heatmap}
|
||||
heatmapLibrary
|
||||
>
|
||||
{markers}
|
||||
</GoogleMapReact>
|
||||
);
|
||||
|
||||
export const gMapHOC = compose(
|
||||
defaultProps({
|
||||
clusterRadius: 60,
|
||||
hoverDistance: 30,
|
||||
options: {
|
||||
minZoom: 3,
|
||||
maxZoom: 15,
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
flex: 1,
|
||||
},
|
||||
}),
|
||||
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
|
||||
// withState so you could change markers if you want
|
||||
withStateSelector('markers', 'setMarkers', () =>
|
||||
createSelector(
|
||||
({ route: { markersCount = 20 } }) => markersCount,
|
||||
markersCount => generateMarkers(markersCount)
|
||||
)),
|
||||
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
|
||||
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
|
||||
withSafeInterval,
|
||||
withState('heatmap', 'setHeatmap', heatmapData),
|
||||
// describe events
|
||||
withHandlers({
|
||||
onChange: ({ setMapParams, setSafeInterval, setHeatmap, mapParams }) =>
|
||||
({ center, zoom, bounds }) => {
|
||||
setMapParams({ center, zoom, bounds });
|
||||
const boundSetHeatmap = setHeatmap.bind(this);
|
||||
setSafeInterval(
|
||||
() => {
|
||||
boundSetHeatmap(
|
||||
generateHeatmapData(mapParams.center.lat, mapParams.center.lng)
|
||||
);
|
||||
},
|
||||
3000
|
||||
);
|
||||
},
|
||||
onChildMouseEnter: ({ setHoveredMarkerId }) =>
|
||||
(hoverKey, { id }) => {
|
||||
setHoveredMarkerId(id);
|
||||
},
|
||||
onChildMouseLeave: ({ setHoveredMarkerId }) =>
|
||||
() => {
|
||||
setHoveredMarkerId(-1);
|
||||
},
|
||||
}),
|
||||
withPropsOnChange(['markers', 'mapParams'], ({
|
||||
markers,
|
||||
mapParams: { bounds },
|
||||
}) => ({
|
||||
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
|
||||
})),
|
||||
withProps(({ hoveredMarkerId }) => ({
|
||||
draggable: hoveredMarkerId === -1,
|
||||
})),
|
||||
withPropsOnChange(['markers'], ({ markers }) => ({
|
||||
markers: markers.map(({ ...markerProps, id }) => (
|
||||
<SimpleMarker key={id} id={id} {...markerProps} />
|
||||
)),
|
||||
}))
|
||||
);
|
||||
|
||||
export default gMapHOC(gMapHeatmap);
|
||||
@ -1,114 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
compose,
|
||||
defaultProps,
|
||||
withHandlers,
|
||||
withState,
|
||||
withContext,
|
||||
withProps,
|
||||
withPropsOnChange,
|
||||
} from 'recompose';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { londonCoords, generateMarkers } from './data/fakeData';
|
||||
|
||||
import GoogleMapReact from '../src';
|
||||
import SimpleMarker from './markers/SimpleMarker';
|
||||
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import { GOOGLE_API_KEY } from './config/Google_API_key';
|
||||
|
||||
export const gMap = (
|
||||
{
|
||||
style,
|
||||
hoverDistance,
|
||||
options,
|
||||
mapParams: { center, zoom },
|
||||
onChange,
|
||||
onChildMouseEnter,
|
||||
onChildMouseLeave,
|
||||
markers,
|
||||
draggable, // hoveredMarkerId,
|
||||
}
|
||||
) => (
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: GOOGLE_API_KEY,
|
||||
}}
|
||||
style={style}
|
||||
options={options}
|
||||
draggable={draggable}
|
||||
hoverDistance={hoverDistance}
|
||||
zoom={zoom}
|
||||
center={center}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
layerTypes={
|
||||
zoom > 12
|
||||
? []
|
||||
: zoom > 10 ? ['TrafficLayer'] : ['TrafficLayer', 'TransitLayer']
|
||||
}
|
||||
>
|
||||
{markers}
|
||||
</GoogleMapReact>
|
||||
);
|
||||
|
||||
export const gMapHOC = compose(
|
||||
defaultProps({
|
||||
clusterRadius: 60,
|
||||
hoverDistance: 30,
|
||||
options: {
|
||||
minZoom: 3,
|
||||
maxZoom: 15,
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
flex: 1,
|
||||
},
|
||||
}),
|
||||
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
|
||||
// withState so you could change markers if you want
|
||||
withStateSelector('markers', 'setMarkers', () =>
|
||||
createSelector(
|
||||
({ route: { markersCount = 20 } }) => markersCount,
|
||||
markersCount => generateMarkers(markersCount)
|
||||
)),
|
||||
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
|
||||
withState('mapParams', 'setMapParams', { center: londonCoords, zoom: 9 }),
|
||||
// describe events
|
||||
withHandlers({
|
||||
onChange: ({ setMapParams }) =>
|
||||
({ center, zoom, bounds }) => {
|
||||
setMapParams({ center, zoom, bounds });
|
||||
},
|
||||
onChildMouseEnter: ({ setHoveredMarkerId }) =>
|
||||
(hoverKey, { id }) => {
|
||||
setHoveredMarkerId(id);
|
||||
},
|
||||
onChildMouseLeave: ({ setHoveredMarkerId }) =>
|
||||
() => {
|
||||
setHoveredMarkerId(-1);
|
||||
},
|
||||
}),
|
||||
withPropsOnChange(['markers', 'mapParams'], ({
|
||||
markers,
|
||||
mapParams: { bounds },
|
||||
}) => ({
|
||||
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
|
||||
})),
|
||||
withProps(({ hoveredMarkerId }) => ({
|
||||
draggable: hoveredMarkerId === -1,
|
||||
})),
|
||||
withPropsOnChange(['markers'], ({ markers }) => ({
|
||||
markers: markers.map(({ ...markerProps, id }) => (
|
||||
<SimpleMarker key={id} id={id} {...markerProps} />
|
||||
)),
|
||||
}))
|
||||
);
|
||||
|
||||
export default gMapHOC(gMap);
|
||||
@ -1,124 +0,0 @@
|
||||
// Example to test the React Reconciler. This example
|
||||
// is 100x faster in development mode,
|
||||
// and 1.5-2x faster with NODE_ENV==='production'
|
||||
|
||||
// The idea was to not draw map children on hovers, but subscribe inside children on hover change
|
||||
// see ./markers/ReactiveMarker source
|
||||
import React from 'react';
|
||||
import {
|
||||
compose,
|
||||
defaultProps,
|
||||
withHandlers,
|
||||
withState,
|
||||
withProps,
|
||||
withPropsOnChange,
|
||||
} from 'recompose';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { susolvkaCoords, generateMarkers } from './data/fakeData';
|
||||
|
||||
import GoogleMapReact from '../src';
|
||||
// import SimpleMarker from './markers/SimpleMarker';
|
||||
import ReactiveMarker from './markers/ReactiveMarker';
|
||||
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import props2Stream from './utils/props2Stream';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import { GOOGLE_API_KEY } from './config/Google_API_key';
|
||||
|
||||
export const gMap = (
|
||||
{
|
||||
style,
|
||||
hoverDistance,
|
||||
options,
|
||||
mapParams: { center, zoom },
|
||||
onChange,
|
||||
onChildMouseEnter,
|
||||
onChildMouseLeave,
|
||||
markers,
|
||||
draggable,
|
||||
}
|
||||
) => (
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: GOOGLE_API_KEY,
|
||||
}}
|
||||
style={style}
|
||||
options={options}
|
||||
draggable={draggable}
|
||||
hoverDistance={hoverDistance}
|
||||
zoom={zoom}
|
||||
center={center}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
experimental
|
||||
>
|
||||
{markers}
|
||||
</GoogleMapReact>
|
||||
);
|
||||
|
||||
export const gMapHOC = compose(
|
||||
defaultProps({
|
||||
clusterRadius: 60,
|
||||
hoverDistance: 30,
|
||||
options: {
|
||||
minZoom: 3,
|
||||
maxZoom: 15,
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
flex: 1,
|
||||
},
|
||||
}),
|
||||
// withState so you could change markers if you want
|
||||
withStateSelector('markers', 'setMarkers', () =>
|
||||
createSelector(
|
||||
({ route: { markersCount = 20 } }) => markersCount,
|
||||
markersCount => generateMarkers(markersCount)
|
||||
)),
|
||||
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
|
||||
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
|
||||
// describe events
|
||||
withHandlers({
|
||||
onChange: ({ setMapParams }) =>
|
||||
({ center, zoom, bounds }) => {
|
||||
setMapParams({ center, zoom, bounds });
|
||||
},
|
||||
onChildMouseEnter: ({ setHoveredMarkerId }) =>
|
||||
(hoverKey, { id }) => {
|
||||
setHoveredMarkerId(id);
|
||||
},
|
||||
onChildMouseLeave: ({ setHoveredMarkerId }) =>
|
||||
() => {
|
||||
setHoveredMarkerId(-1);
|
||||
},
|
||||
}),
|
||||
withPropsOnChange(['markers', 'mapParams'], ({
|
||||
markers,
|
||||
mapParams: { bounds },
|
||||
}) => ({
|
||||
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
|
||||
})),
|
||||
withProps(({ hoveredMarkerId }) => ({
|
||||
draggable: hoveredMarkerId === -1,
|
||||
})),
|
||||
props2Stream('hoveredMarkerId'),
|
||||
withPropsOnChange(['markers', 'hoveredMarkerId$'], ({
|
||||
markers,
|
||||
hoveredMarkerId$,
|
||||
}) => ({
|
||||
markers: markers.map(({ ...markerProps, id }) => (
|
||||
<ReactiveMarker
|
||||
key={id}
|
||||
id={id}
|
||||
hoveredMarkerId$={hoveredMarkerId$}
|
||||
{...markerProps}
|
||||
/>
|
||||
)),
|
||||
}))
|
||||
);
|
||||
|
||||
export default gMapHOC(gMap);
|
||||
@ -1,111 +0,0 @@
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
compose,
|
||||
defaultProps,
|
||||
withHandlers,
|
||||
withState,
|
||||
withContext,
|
||||
withProps,
|
||||
withPropsOnChange,
|
||||
} from 'recompose';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { susolvkaCoords, generateMarkers } from './data/fakeData';
|
||||
|
||||
import GoogleMapReact from '../src';
|
||||
import SimpleMarker from './markers/SimpleMarker';
|
||||
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import { GOOGLE_API_KEY } from './config/Google_API_key';
|
||||
|
||||
export const gMapResizable = (
|
||||
{
|
||||
style,
|
||||
hoverDistance,
|
||||
options,
|
||||
mapParams: { center, zoom },
|
||||
onChange,
|
||||
onChildMouseEnter,
|
||||
onChildMouseLeave,
|
||||
markers,
|
||||
draggable, // hoveredMarkerId,
|
||||
}
|
||||
) => (
|
||||
<GoogleMapReact
|
||||
bootstrapURLKeys={{
|
||||
key: GOOGLE_API_KEY,
|
||||
}}
|
||||
style={style}
|
||||
options={options}
|
||||
draggable={draggable}
|
||||
hoverDistance={hoverDistance}
|
||||
zoom={zoom}
|
||||
center={center}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
resetBoundsOnResize
|
||||
>
|
||||
{markers}
|
||||
</GoogleMapReact>
|
||||
);
|
||||
|
||||
export const gMapHOC = compose(
|
||||
defaultProps({
|
||||
clusterRadius: 60,
|
||||
hoverDistance: 30,
|
||||
options: {
|
||||
minZoom: 3,
|
||||
maxZoom: 15,
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
flex: 1,
|
||||
},
|
||||
}),
|
||||
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
|
||||
// withState so you could change markers if you want
|
||||
withStateSelector('markers', 'setMarkers', () =>
|
||||
createSelector(
|
||||
({ route: { markersCount = 20 } }) => markersCount,
|
||||
markersCount => generateMarkers(markersCount)
|
||||
)),
|
||||
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
|
||||
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
|
||||
// describe events
|
||||
withHandlers({
|
||||
onChange: ({ setMapParams }) =>
|
||||
({ center, zoom, bounds }) => {
|
||||
setMapParams({ center, zoom, bounds });
|
||||
},
|
||||
onChildMouseEnter: ({ setHoveredMarkerId }) =>
|
||||
(hoverKey, { id }) => {
|
||||
setHoveredMarkerId(id);
|
||||
},
|
||||
onChildMouseLeave: ({ setHoveredMarkerId }) =>
|
||||
() => {
|
||||
setHoveredMarkerId(-1);
|
||||
},
|
||||
}),
|
||||
withPropsOnChange(['markers', 'mapParams'], ({
|
||||
markers,
|
||||
mapParams: { bounds },
|
||||
}) => ({
|
||||
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
|
||||
})),
|
||||
withProps(({ hoveredMarkerId }) => ({
|
||||
draggable: hoveredMarkerId === -1,
|
||||
})),
|
||||
withPropsOnChange(['markers'], ({ markers }) => ({
|
||||
markers: markers.map(({ ...markerProps, id }) => (
|
||||
<SimpleMarker key={id} id={id} {...markerProps} />
|
||||
)),
|
||||
}))
|
||||
);
|
||||
|
||||
export default gMapHOC(gMapResizable);
|
||||
@ -1,58 +0,0 @@
|
||||
import { Link } from 'react-router';
|
||||
import React, { Component } from 'react';
|
||||
import { compose, defaultProps } from 'recompose';
|
||||
|
||||
import layoutStyles from './Layout.sass';
|
||||
|
||||
// for hmr to work I need the first class to extend Component
|
||||
/* eslint-disable react/prefer-stateless-function */
|
||||
export class Layout extends Component {
|
||||
render() {
|
||||
const {
|
||||
styles: { layout, header, main, footer, logo, links },
|
||||
} = this.props;
|
||||
return (
|
||||
<div className={layout}>
|
||||
<header className={header}>
|
||||
<div className={links}>
|
||||
<Link to="/">Multi Markers</Link>
|
||||
<Link to="/layers">With layers</Link>
|
||||
<Link to="/hoverunoptim">Hover unoptim</Link>
|
||||
<Link to="/hoveroptim">Hover optim</Link>
|
||||
<Link to="/resizable">Resizable Map</Link>
|
||||
<Link to="/heatmap">Heatmap</Link>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/istarkov/google-map-clustering-example">
|
||||
Star at github.com
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<main className={main}>
|
||||
{this.props.children}
|
||||
</main>
|
||||
<footer className={footer}>
|
||||
<div>
|
||||
<a href="https://github.com/istarkov">
|
||||
Ivan Starkov
|
||||
</a>
|
||||
</div>
|
||||
<div className={logo} />
|
||||
<div>
|
||||
<a href="https://twitter.com/icelabaratory">
|
||||
@icelabaratory
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const layoutHOC = compose(
|
||||
defaultProps({
|
||||
styles: layoutStyles,
|
||||
})
|
||||
);
|
||||
|
||||
export default layoutHOC(Layout);
|
||||
@ -1,44 +0,0 @@
|
||||
.layout
|
||||
display: flex
|
||||
min-height: 90vh
|
||||
flex-direction: column
|
||||
margin: 0 1px 0 1px
|
||||
width: 100vw
|
||||
.header
|
||||
height: 2em
|
||||
background-color: #004336
|
||||
color: #fff
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
padding: 0 10px 0 10px
|
||||
a
|
||||
color: #fff
|
||||
|
||||
.links
|
||||
a
|
||||
margin-right: 20px
|
||||
|
||||
.logo
|
||||
width: 1.3em
|
||||
height: 1.3em
|
||||
margin: 0.3em
|
||||
background-size: contain
|
||||
background-repeat: no-repeat
|
||||
background-image: url('https://avatars2.githubusercontent.com/u/5077042?v=3&s=40')
|
||||
|
||||
.main
|
||||
flex: 1
|
||||
resize: both
|
||||
overflow: auto
|
||||
display: flex
|
||||
|
||||
.footer
|
||||
height: 2em
|
||||
background-color: #004336
|
||||
color: #fff
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
a
|
||||
color: #fff
|
||||
@ -1,30 +0,0 @@
|
||||
/* eslint-disable import/no-named-as-default */
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
|
||||
import 'normalize.css/normalize.css';
|
||||
|
||||
import GMap from './GMap';
|
||||
import Layout from './Layout';
|
||||
import GMapOptim from './GMapOptim';
|
||||
import GMapLayers from './GMapLayers';
|
||||
import GMapHeatmap from './GMapHeatmap';
|
||||
import GMapResizable from './GMapResizable';
|
||||
|
||||
import './Main.sass';
|
||||
|
||||
const mountNode = document.getElementById('app');
|
||||
|
||||
render(
|
||||
<Router history={browserHistory}>
|
||||
<Route path="/" component={Layout}>
|
||||
<Route markersCount={50} path="hoverunoptim" component={GMap} />
|
||||
<Route markersCount={50} path="layers" component={GMapLayers} />
|
||||
<Route markersCount={50} path="hoveroptim" component={GMapOptim} />
|
||||
<Route markersCount={20} path="resizable" component={GMapResizable} />
|
||||
<Route markersCount={20} path="heatmap" component={GMapHeatmap} />
|
||||
<IndexRoute markersCount={20} component={GMap} />
|
||||
</Route>
|
||||
</Router>,
|
||||
mountNode
|
||||
);
|
||||
@ -1,3 +0,0 @@
|
||||
html, body
|
||||
height: 100%
|
||||
font-size: 14px
|
||||
@ -1,2 +0,0 @@
|
||||
// use your own google maps API key with localhost permissions below
|
||||
export const GOOGLE_API_KEY = '';
|
||||
@ -1,50 +0,0 @@
|
||||
import path from 'path'; // eslint-disable-line no-var
|
||||
import autoprefixer from 'autoprefixer'; // eslint-disable-line no-var
|
||||
import webpack from 'webpack';
|
||||
|
||||
export default {
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
postcss: [autoprefixer({ browsers: ['last 2 versions'] })],
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(
|
||||
process.env.NODE_ENV
|
||||
? {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
}
|
||||
: {}
|
||||
),
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.sass$/,
|
||||
loaders: [
|
||||
'style-loader',
|
||||
'css-loader?modules&importLoaders=2&localIdentName=[name]__[local]',
|
||||
'postcss-loader',
|
||||
'sass-loader?precision=10&indentedSyntax=sass',
|
||||
],
|
||||
include: [
|
||||
path.join(__dirname, '../../src'),
|
||||
path.join(__dirname, '..'),
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
loaders: [
|
||||
'style-loader',
|
||||
'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]',
|
||||
'postcss-loader',
|
||||
],
|
||||
include: [
|
||||
path.join(__dirname, '..'),
|
||||
path.join(__dirname, '../../node_modules'),
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
loaders: ['url-loader?limit=7000'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -1,73 +0,0 @@
|
||||
export const susolvkaCoords = { lat: 60.814305, lng: 47.051773 };
|
||||
|
||||
export const londonCoords = { lat: 51.508411, lng: -0.125364 };
|
||||
|
||||
export const generateMarkers = count =>
|
||||
[...Array(count)].fill(0).map((__, index) => ({
|
||||
// fill(0) for loose mode
|
||||
id: index,
|
||||
lat: susolvkaCoords.lat +
|
||||
0.01 *
|
||||
index *
|
||||
Math.sin(30 * Math.PI * index / 180) *
|
||||
Math.cos(50 * Math.PI * index / 180) +
|
||||
Math.sin(5 * index / 180),
|
||||
lng: susolvkaCoords.lng +
|
||||
0.01 *
|
||||
index *
|
||||
Math.cos(70 + 23 * Math.PI * index / 180) *
|
||||
Math.cos(50 * Math.PI * index / 180) +
|
||||
Math.sin(5 * index / 180),
|
||||
}));
|
||||
|
||||
export const heatmapData = {
|
||||
positions: [
|
||||
{
|
||||
lat: 60.714305,
|
||||
lng: 47.051773,
|
||||
},
|
||||
{
|
||||
lat: 60.734305,
|
||||
lng: 47.061773,
|
||||
},
|
||||
{
|
||||
lat: 60.754305,
|
||||
lng: 47.081773,
|
||||
},
|
||||
{
|
||||
lat: 60.774305,
|
||||
lng: 47.101773,
|
||||
},
|
||||
{
|
||||
lat: 60.804305,
|
||||
lng: 47.111773,
|
||||
},
|
||||
],
|
||||
options: {
|
||||
radius: 20,
|
||||
opacity: 0.7,
|
||||
},
|
||||
};
|
||||
|
||||
function getRandomNumberBetween(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export const generateHeatmapData = (lat, lng) => {
|
||||
const newFakeReadings = x => {
|
||||
const newReadings = [];
|
||||
|
||||
for (let i = 0; i <= x; i++) {
|
||||
newReadings.push({
|
||||
weight: getRandomNumberBetween(0.1, 4),
|
||||
lat: getRandomNumberBetween(lat - 1, lat + 1),
|
||||
lng: getRandomNumberBetween(lng - 1, lng + 1),
|
||||
});
|
||||
}
|
||||
return newReadings;
|
||||
};
|
||||
return {
|
||||
positions: newFakeReadings(10),
|
||||
options: heatmapData.options,
|
||||
};
|
||||
};
|
||||
@ -1,78 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Motion, spring } from 'react-motion';
|
||||
import { compose, defaultProps, withPropsOnChange, pure } from 'recompose';
|
||||
|
||||
import clusterMarkerStyles from './ClusterMarker.sass';
|
||||
|
||||
export const clusterMarker = (
|
||||
{
|
||||
styles,
|
||||
text,
|
||||
hovered,
|
||||
$hover,
|
||||
defaultMotionStyle,
|
||||
motionStyle,
|
||||
}
|
||||
) => (
|
||||
<Motion defaultStyle={defaultMotionStyle} style={motionStyle}>
|
||||
{({ scale }) => (
|
||||
<div
|
||||
className={styles.marker}
|
||||
style={{
|
||||
transform: `translate3D(0,0,0) scale(${scale}, ${scale})`,
|
||||
zIndex: hovered || $hover ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<div className={styles.text}>
|
||||
{text}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
|
||||
export const clusterMarkerHOC = compose(
|
||||
defaultProps({
|
||||
text: '0',
|
||||
styles: clusterMarkerStyles,
|
||||
initialScale: 0.6,
|
||||
defaultScale: 1,
|
||||
hoveredScale: 1.15,
|
||||
hovered: false,
|
||||
stiffness: 320,
|
||||
damping: 7,
|
||||
precision: 0.001,
|
||||
}),
|
||||
// pure optimization can cause some effects you don't want,
|
||||
// don't use it in development for markers
|
||||
pure,
|
||||
withPropsOnChange(['initialScale'], ({
|
||||
initialScale,
|
||||
defaultScale,
|
||||
$prerender,
|
||||
}) => ({
|
||||
initialScale,
|
||||
defaultMotionStyle: { scale: $prerender ? defaultScale : initialScale },
|
||||
})),
|
||||
withPropsOnChange(['hovered', '$hover'], ({
|
||||
hovered,
|
||||
$hover,
|
||||
hoveredScale,
|
||||
defaultScale,
|
||||
stiffness,
|
||||
damping,
|
||||
precision,
|
||||
}) => ({
|
||||
$hover,
|
||||
hovered,
|
||||
motionStyle: {
|
||||
scale: spring(hovered || $hover ? hoveredScale : defaultScale, {
|
||||
stiffness,
|
||||
damping,
|
||||
precision,
|
||||
}),
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
export default clusterMarkerHOC(clusterMarker);
|
||||
@ -1,28 +0,0 @@
|
||||
@function stripUnits($number)
|
||||
@return $number / ($number * 0 + 1)
|
||||
|
||||
$marker-width: 40px !default
|
||||
$marker-height: 40px !default
|
||||
$marker-border-width: 5px !default
|
||||
$marker-font-size: 14px !default
|
||||
|
||||
|
||||
.marker
|
||||
position: absolute
|
||||
cursor: pointer
|
||||
width: $marker-width
|
||||
height: $marker-height
|
||||
left: -$marker-width / 2
|
||||
top: -$marker-height / 2
|
||||
|
||||
border: $marker-border-width solid #004336
|
||||
border-radius: 50%
|
||||
background-color: white
|
||||
text-align: center
|
||||
color: #333
|
||||
|
||||
font-size: $marker-font-size
|
||||
font-weight: bold
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import { compose } from 'recompose';
|
||||
import 'rxjs/add/operator/filter';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/scan';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import SimpleMarker from './SimpleMarker';
|
||||
|
||||
import stream2Props from '../utils/stream2Props';
|
||||
|
||||
export const reactiveMarker = props => <SimpleMarker {...props} />;
|
||||
|
||||
export const reactiveMarkerHOC = compose(
|
||||
stream2Props(({ id, hoveredMarkerId$ }) =>
|
||||
hoveredMarkerId$
|
||||
.map(hoveredMarkerId => hoveredMarkerId === id)
|
||||
.distinctUntilChanged()
|
||||
.map(v => ({ hovered: v })))
|
||||
);
|
||||
|
||||
export default reactiveMarkerHOC(reactiveMarker);
|
||||
@ -1,47 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Motion } from 'react-motion';
|
||||
import { compose, defaultProps, getContext } from 'recompose';
|
||||
|
||||
import { clusterMarkerHOC } from './ClusterMarker';
|
||||
|
||||
import simpleMarkerStyles from './SimpleMarker.sass';
|
||||
|
||||
export const simpleMarker = (
|
||||
{
|
||||
styles,
|
||||
hovered,
|
||||
$hover,
|
||||
defaultMotionStyle,
|
||||
motionStyle,
|
||||
// hello,
|
||||
} // console.log('hello', hello),
|
||||
) => (
|
||||
<Motion defaultStyle={defaultMotionStyle} style={motionStyle}>
|
||||
{({ scale }) => (
|
||||
<div
|
||||
className={styles.marker}
|
||||
style={{
|
||||
transform: `translate3D(0,0,0) scale(${scale}, ${scale})`,
|
||||
zIndex: hovered || $hover ? 1 : 0,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
|
||||
export const simpleMarkerHOC = compose(
|
||||
defaultProps({
|
||||
styles: simpleMarkerStyles,
|
||||
initialScale: 0.6,
|
||||
defaultScale: 0.6,
|
||||
hoveredScale: 0.7,
|
||||
}),
|
||||
getContext({
|
||||
hello: PropTypes.string,
|
||||
}),
|
||||
// resuse HOC
|
||||
clusterMarkerHOC
|
||||
);
|
||||
|
||||
export default simpleMarkerHOC(simpleMarker);
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
$markerWidth: 49px
|
||||
$markerHeight: 64px
|
||||
|
||||
$originX: $markerWidth * .5
|
||||
$originY: $markerHeight
|
||||
|
||||
.marker
|
||||
background-image: url('./mapIcon.svg')
|
||||
position: absolute
|
||||
cursor: pointer
|
||||
width: $markerWidth
|
||||
height: $markerHeight
|
||||
top: -$originY
|
||||
left: -$originX
|
||||
transform-origin: $originX $originY
|
||||
margin: 0
|
||||
padding: 0
|
||||
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="49px" height="64px" viewBox="0 0 49 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.0.4 (8054) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icons 2</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="map58-(1)" sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
|
||||
<path d="M-0.0450631868,23.2602268 C-0.0450631868,28.4922598 1.69858516,33.3156041 4.64331593,37.2038351 L23.4492555,62.0272289 L42.2550659,37.2038351 C45.2000549,33.3156041 46.9434451,28.4923876 46.9434451,23.2602268 C46.9434451,10.4151052 36.4235797,0 23.4491264,0 C10.4748022,0 -0.0450631868,10.4151052 -0.0450631868,23.2602268 Z" id="Path" stroke="#FFFFFF" stroke-width="2" fill="#4990E2" sketch:type="MSShapeGroup"></path>
|
||||
<path d="M23.4491264,38.7668742 C14.7994478,38.7668742 7.78650549,31.8240247 7.78650549,23.2602268 C7.78650549,14.6966845 14.7994478,7.75332371 23.4491264,7.75332371 C32.0990632,7.75332371 39.1117473,14.6966845 39.1117473,23.2602268 C39.1117473,31.8241526 32.0991923,38.7668742 23.4491264,38.7668742 L23.4491264,38.7668742 Z" id="Shape" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,17 +0,0 @@
|
||||
/* eslint-disable max-len */
|
||||
// `npm bin`/mocha --compilers js:babel-register --require babel-polyfill --reporter min --watch './develop/**/*.spec.js'
|
||||
/* eslint-enable max-len */
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
describe('Playground', () => {
|
||||
it('Play', async () => {
|
||||
const comparator = (a, b) => a === b;
|
||||
const props$ = new BehaviorSubject(1).distinctUntilChanged(comparator);
|
||||
|
||||
props$.subscribe(v => console.log(v)); // eslint-disable-line
|
||||
props$.next(1);
|
||||
props$.next(2);
|
||||
props$.next(1);
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const createEagerElementUtil = (
|
||||
hasKey,
|
||||
isReferentiallyTransparent,
|
||||
type,
|
||||
props,
|
||||
children
|
||||
) => {
|
||||
if (!hasKey && isReferentiallyTransparent) {
|
||||
if (children) {
|
||||
return type({ ...props, children });
|
||||
}
|
||||
return type(props);
|
||||
}
|
||||
|
||||
const Component = type;
|
||||
|
||||
if (children) {
|
||||
return <Component {...props}>{children}</Component>;
|
||||
}
|
||||
|
||||
return <Component {...props} />;
|
||||
};
|
||||
|
||||
export default createEagerElementUtil;
|
||||
@ -1,13 +0,0 @@
|
||||
import createEagerElementUtil from './createEagerElementUtil';
|
||||
import isReferentiallyTransparentFunctionComponent
|
||||
from './isReferentiallyTransparentFunctionComponent';
|
||||
|
||||
const createFactory = type => {
|
||||
const isReferentiallyTransparent = isReferentiallyTransparentFunctionComponent(
|
||||
type
|
||||
);
|
||||
return (p, c) =>
|
||||
createEagerElementUtil(false, isReferentiallyTransparent, type, p, c);
|
||||
};
|
||||
|
||||
export default createFactory;
|
||||
@ -1,29 +0,0 @@
|
||||
import { wrapDisplayName } from 'recompose';
|
||||
|
||||
const createHelper = (
|
||||
func,
|
||||
helperName,
|
||||
setDisplayName = true,
|
||||
noArgs = false
|
||||
) => {
|
||||
if (process.env.NODE_ENV !== 'production' && setDisplayName) {
|
||||
if (noArgs) {
|
||||
return BaseComponent => {
|
||||
const Component = func(BaseComponent);
|
||||
Component.displayName = wrapDisplayName(BaseComponent, helperName);
|
||||
return Component;
|
||||
};
|
||||
}
|
||||
|
||||
return (...args) =>
|
||||
BaseComponent => {
|
||||
const Component = func(...args)(BaseComponent);
|
||||
Component.displayName = wrapDisplayName(BaseComponent, helperName);
|
||||
return Component;
|
||||
};
|
||||
}
|
||||
|
||||
return func;
|
||||
};
|
||||
|
||||
export default createHelper;
|
||||
@ -1,17 +0,0 @@
|
||||
const isClassComponent = Component =>
|
||||
Boolean(
|
||||
Component &&
|
||||
Component.prototype &&
|
||||
typeof Component.prototype.isReactComponent === 'object'
|
||||
);
|
||||
|
||||
const isReferentiallyTransparentFunctionComponent = Component =>
|
||||
Boolean(
|
||||
typeof Component === 'function' &&
|
||||
!isClassComponent(Component) &&
|
||||
!Component.defaultProps &&
|
||||
!Component.contextTypes &&
|
||||
!Component.propTypes
|
||||
);
|
||||
|
||||
export default isReferentiallyTransparentFunctionComponent;
|
||||
@ -1,13 +0,0 @@
|
||||
// https://github.com/acdlite/recompose/blob/master/src/packages/recompose/utils/omit.js
|
||||
const omit = (obj, keys) => {
|
||||
const { ...rest } = obj;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key in rest) {
|
||||
delete rest[key];
|
||||
}
|
||||
}
|
||||
return rest;
|
||||
};
|
||||
|
||||
export default omit;
|
||||
@ -1,32 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import omit from './omit';
|
||||
import createHelper from './createHelper';
|
||||
import createEagerFactory from './createEagerFactory';
|
||||
|
||||
const prop2Stream = (propName, comparator = (a, b) => a === b) =>
|
||||
BaseComponent => {
|
||||
const factory = createEagerFactory(BaseComponent);
|
||||
return class extends Component {
|
||||
props$ = new BehaviorSubject(this.props[propName]).distinctUntilChanged(
|
||||
comparator
|
||||
);
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
this.props$.next(nextProps[propName]);
|
||||
}
|
||||
|
||||
render() {
|
||||
return factory({
|
||||
...omit(this.props, [propName]),
|
||||
[`${propName}$`]: this.props$,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default createHelper(prop2Stream, 'prop2Stream');
|
||||
@ -1,10 +0,0 @@
|
||||
const ptInSect = (x, a, b) => (x - a) * (x - b) <= 0;
|
||||
|
||||
export default ({ nw, se }, pt) => {
|
||||
const lngs = nw.lng < se.lng
|
||||
? [[nw.lng, se.lng]]
|
||||
: [[nw.lng, 180], [-180, se.lng]];
|
||||
|
||||
return ptInSect(pt.lat, se.lat, nw.lat) &&
|
||||
lngs.some(([lngFrom, lngTo]) => ptInSect(pt.lng, lngFrom, lngTo));
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import createHelper from './createHelper';
|
||||
import createEagerFactory from './createEagerFactory';
|
||||
|
||||
// if stream prop will change this will fail,
|
||||
// this is expected behavior
|
||||
const stream2Props = props2Stream =>
|
||||
BaseComponent => {
|
||||
const factory = createEagerFactory(BaseComponent);
|
||||
return class extends Component {
|
||||
state = {};
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.subscription = props2Stream(this.props).subscribe(value =>
|
||||
this.setState({ value }));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
render() {
|
||||
return factory({
|
||||
...this.props,
|
||||
...this.state.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default createHelper(stream2Props, 'stream2Props');
|
||||
@ -1,48 +0,0 @@
|
||||
import { createElement, Component } from 'react';
|
||||
|
||||
const safeTimerFactory = (setFn, clearFn, propName, hocName) =>
|
||||
Target => {
|
||||
class SafeTimer extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.unsubscribers = [];
|
||||
this[propName] = this[propName].bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unsubscribers.forEach(unsubscribe => unsubscribe());
|
||||
|
||||
this.unsubscribers = [];
|
||||
}
|
||||
|
||||
[propName](...args) {
|
||||
const id = setFn(...args);
|
||||
const unsubscriber = () => clearFn(id);
|
||||
|
||||
this.unsubscribers.push(unsubscriber);
|
||||
|
||||
return unsubscriber;
|
||||
}
|
||||
|
||||
render() {
|
||||
return createElement(Target, {
|
||||
...this.props,
|
||||
[propName]: this[propName],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
SafeTimer.displayName = `${hocName}`;
|
||||
}
|
||||
|
||||
return SafeTimer;
|
||||
};
|
||||
|
||||
export default safeTimerFactory(
|
||||
global.setInterval,
|
||||
global.clearInterval,
|
||||
'setSafeInterval',
|
||||
'withSafeInterval'
|
||||
);
|
||||
@ -1,45 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import createHelper from './createHelper';
|
||||
import createEagerFactory from './createEagerFactory';
|
||||
|
||||
const withStateSelector = (stateName, stateUpdaterName, selectorFactory) =>
|
||||
BaseComponent => {
|
||||
const factory = createEagerFactory(BaseComponent);
|
||||
return class extends Component {
|
||||
selector = selectorFactory();
|
||||
state = {
|
||||
stateValue: this.selector(this.props),
|
||||
};
|
||||
|
||||
updateStateValue = (updateFn, callback) =>
|
||||
this.setState(
|
||||
({ stateValue }) => ({
|
||||
stateValue: typeof updateFn === 'function'
|
||||
? updateFn(stateValue)
|
||||
: updateFn,
|
||||
}),
|
||||
callback
|
||||
);
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
// reselect memoize result
|
||||
const nextStateValue = this.selector(nextProps);
|
||||
if (nextStateValue !== this.state.stateValue) {
|
||||
this.setState({
|
||||
stateValue: nextStateValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return factory({
|
||||
...this.props,
|
||||
[stateName]: this.state.stateValue,
|
||||
[stateUpdaterName]: this.updateStateValue,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default createHelper(withStateSelector, 'withStateSelector');
|
||||
5
example/README.md
Normal file
5
example/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
It is linked to the google-map-react package in the parent directory for development purposes.
|
||||
|
||||
You can run `yarn install` and then `yarn start` to test your package.
|
||||
31
example/package.json
Normal file
31
example/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "google-map-react-example",
|
||||
"homepage": ".",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
|
||||
"build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
|
||||
"test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
|
||||
"eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"google-map-react": "link:..",
|
||||
"react": "link:../node_modules/react",
|
||||
"react-dom": "link:../node_modules/react-dom",
|
||||
"react-scripts": "link:../node_modules/react-scripts",
|
||||
"styled-components": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-syntax-object-rest-spread": "^7.8.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
BIN
example/public/favicon.ico
Normal file
BIN
example/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
48
example/public/index.html
Normal file
48
example/public/index.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>google-map-react</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
|
||||
<div id="root"></div>
|
||||
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
15
example/public/manifest.json
Normal file
15
example/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "google-map-react",
|
||||
"name": "google-map-react",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
886
example/public/places.json
Normal file
886
example/public/places.json
Normal file
@ -0,0 +1,886 @@
|
||||
{
|
||||
"html_attributions": [],
|
||||
"next_page_token": "CpQCDwEAAJwdqLU_q4CMC9j-DOFIiwmwpP8ICAF_lHHKpWrNLfYsLS0hubKFTbau5sp7Tc_vC2bftXqZSRfq4uMk34-nE1f3weBRv77ZvsUhQn1MvJCfW6pNFA6zrDP_lPLBQZVrGVePIGhW8g485WnZdAHUyKpr4jjJ6hiOh2R6MxVBTYHyRuy5mDa-NGx4zQXDsHGSejOPH6BmPUoyfb-2MmRo1kcYABkBZRAaXpDmTtoXLmX8GxELbMOOAjEJ8Vzxdk-4GQkzoDYB1YwHBR1JXzxlawX0DdUyaJKjLWGjvt8GOwF8d9KVTF0r528jDCheFjvRgxOGMeYq1IrUx9lBofHH4sAX2plD6BGumN40mdBkDKVMEhC8pyLxL6t51mflBEc_WkF6GhTsH0kHwldrZYDnyUZ78TU9zeiiqg",
|
||||
"results": [{
|
||||
"formatted_address": "3818 Sunset Blvd, Los Angeles, CA 90026, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.091158,
|
||||
"lng": -118.2795188
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.09258172989272,
|
||||
"lng": -118.2780556701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.08988207010728,
|
||||
"lng": -118.2807553298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "1113d3dd7339f965caae39387dd808a9e877bc2e",
|
||||
"name": "Flore Vegan",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 2336,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/100540448898812651487/photos\">Roman Roze</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAA69UVwaJnUQXQUSSX9IfB3b29opNIohkexsGAGoHTD5Lyg24lhpBtaiNlrgihstR-k7Su9Vgbc8-eE5qHEdeLVY1QTfiuyS9TPp3e2GMM_grW2FtrgrFQGtMJSeJ336cPEhCVHYfFzoOgrrKdXlk34rJiGhSXSv_XG1q1CtOrWJjWQrxJmLvIPg",
|
||||
"width": 3504
|
||||
}],
|
||||
"place_id": "ChIJj62e80jHwoARusJT4mjohWw",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAM9_YQ6Dt9T69zucczidzOd6HU2vmzaXvTG-lJ89KyBlJVBJ0aEfar2Exre4iDWKHjExUshYSpAXEzA-YqotVnOt4XznKY_vkD520XK5nzFz5v5IefUe6FDBqZPzYlxRDEhAgQvwzNjwC49WWlyoMKza5GhS6r-VIl1lXdMl_JEW67yL7fPkZdg",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "1700 Sunset Blvd, Los Angeles, CA 90026, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0771192,
|
||||
"lng": -118.2587199
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.07856197989273,
|
||||
"lng": -118.2573112201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.07586232010728,
|
||||
"lng": -118.2600108798928
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "d8bc8d867ccf72cbc552e100238a0820628963ee",
|
||||
"name": "Sage Plant Based Bistro and Brewery Echo Park",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3024,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/105756638182226336287/photos\">Anurag Singhai</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAW6v0EyZa77vrG5Aq8zbnU1sR7pljqfoDpSxXhISFgWoYWLkFY5hh7YCFbzYLj1XzflTJOrCXJa-q5jPT4L0vMY8cjXrhCzB5y7Z--qJTWOO_NxaRUBbB5QhpxyUT-R6tEhBru_ZZ_xcCxFueYrRxI6pFGhRgiExnWWOhU2Ii_NnW6M8R4xs4IQ",
|
||||
"width": 4032
|
||||
}],
|
||||
"place_id": "ChIJ6T9ggBvHwoARc3aegK3PBe0",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAy5CT3sE8bRlADeIeMYtC7NRdE58vKOCjOvhZNUs0QwBD7kSS6WIfo3wmxvt4EGZm5TJ6WdqOCRSnnnFoXAVpz9F3EyZPosUDCN2LJIvtYxwS3BYkwh6uVQRSj-OPLak6EhBRf8xOpcuc5WWWBgGNbGGbGhSYiomy4iNV-g_KzzqNrpymm3MxQw",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "8284 Melrose Ave, Los Angeles, CA 90046, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.083527,
|
||||
"lng": -118.370157
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.08495787989272,
|
||||
"lng": -118.3688093201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.08225822010727,
|
||||
"lng": -118.3715089798927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "8529e7504ec45e2f380310882a3bd05fe050d701",
|
||||
"name": "Crossroads Kitchen",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 446,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/105056983516844525128/photos\">Crossroads Vegan Restaurant</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAeNvmtrTLNo95XHaHxur4Lc3CSCbtl8zUk1cbjlFdDSpMupFJ0LXRP7iaqHQeP0N8srb3P9NzwI0jrASIvj7dn-8b0KkmjvgeAmETi9sDAEwQVOui2yIJd5yzzJN3LD_bEhCCaBWZ_g1fJHyzVzz9c18eGhTSCsqJIwmOF_xRWfhPBoFUO86y1A",
|
||||
"width": 720
|
||||
}],
|
||||
"place_id": "ChIJhZy9Gba-woARQLEEpEN46uw",
|
||||
"price_level": 3,
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAAU-AZsWP9B3I6f7bIB2lho68meak73rt_fxdoJug-tXoqRil2guPjWxQmycp2dPFKXZNG5azOwT-rEy9vJtXQf44uVj45j_ohS6ietOXClyyAppqZt_ngGVISdtcNvrFaEhDYSDnUEsrd7GxTol1wHfJ4GhRsQAnur588F9jnXy0S4IYaiaEAQA",
|
||||
"types": [
|
||||
"bar",
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "4319 Sunset Blvd, Los Angeles, CA 90029, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0951843,
|
||||
"lng": -118.283107
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.09646662989272,
|
||||
"lng": -118.2818451701072
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.09376697010727,
|
||||
"lng": -118.2845448298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "287049135d7d1ab1bfd2de8d7654afcc97cfee77",
|
||||
"name": "My Vegan Gold",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 667,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/117593961088704552381/photos\">Sayamon Riddang</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAABpfiLbCymEfhpZDl0vsV5ZjC7UDVT-GQAryHJkQ1rZHakstWk4Cugb0HrxU3dsdqbjxvKU9gxVHsZjxToVuMwRw_dHWdwIPFNl-VGYtipa8Z6Uzf6RzmS2tDje45jzTREhD1E_zkXr3Z6v8wWLzDDxwlGhS8VB8VsaT96ycvEwdkjhzmNKSfDg",
|
||||
"width": 1000
|
||||
}],
|
||||
"place_id": "ChIJh6ttFUzHwoAR9p8Wi_B8AbI",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAUR_MLHOu0gPb-77cvoygt4blzXX2PtoI7PRfh8mqqOgx8MPgpclf8PcfCXkEvAfDxu4wafz5l1jBDTPTntDz3Epf0dJxilKvshezCkwMXdlIoZjBZwW8n6BMU5WKGCjXEhBd8OVC5aJ7Qc_S3skTq1KnGhQNTUFaUZAnY5r37iUSLCT59zfhrw",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "4442, 1769 Hillhurst Ave, Los Angeles, CA 90027, United States",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.1033401,
|
||||
"lng": -118.2875469
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.10468952989272,
|
||||
"lng": -118.2861405701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.10198987010727,
|
||||
"lng": -118.2888402298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "1401d50571af0bd7fc0267e6a3635bfb3d3dbda2",
|
||||
"name": "Green Leaves Vegan",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3168,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/118194982700766373715/photos\">Carl Afable</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAz4NOaoeJczV-BFF7HYE-unBeYl8bud0vmkVtYxCv-lKLYo1YnAgcf-Cisr4BQypE9WbaUwm157PYyxLkWkQ1CwVcP7bXtV6QAdNegOXB8QlWAEPR95Q6lm4NQ71B3Vm2EhD2nTXqDhitEm0BrjyRh3JxGhRYCJXLJCF4AWJiBXk5fcdcjylX_g",
|
||||
"width": 4752
|
||||
}],
|
||||
"place_id": "ChIJ2RKN_rHAwoARIGWYxAgzzyQ",
|
||||
"price_level": 1,
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAAtM6n50gnrmJYk2qR_1uslm5ILIFgla8_RVtkm5pLzg9pMV-UcNQ3LzYSc6iM2vm6rbIzSvmjYMvWZ0Qy0AnVyIhb0kYHi1HpNFbYF3aD5cxH2hyHuB9qbn_OY_7vTpWEEhDTEJ002c2Pt-QTBNerb9iOGhRnv28Ti7KlI3gYVtBKG5XKOju9Zg",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "707 E 10th St, Los Angeles, CA 90021, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.035798,
|
||||
"lng": -118.251288
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.03709272989273,
|
||||
"lng": -118.2499997201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.03439307010728,
|
||||
"lng": -118.2526993798928
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "01c75569ea2c5ee7ae29e91990ff4d872bc1ca5b",
|
||||
"name": "The Vegan Joint",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 680,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/106918371964521288041/photos\">The Vegan Joint (Downtown)</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAGdHRzCGTwQ0_tc-fWo9wfezr51jbxPQ7IH1Gsc9OCRxoIijxg110YoNAaCTQSefXtCVdiH2xYcifKokVtnwxp01tE6J888NxT_MA1zpzxiM5ZM3MgJ9OxeJERDyE8jT0EhBSS5kv0YX7tmAxiUFHPOEXGhT2ZyiB6uRAfzpKchXvfOxyvA7S2Q",
|
||||
"width": 1906
|
||||
}],
|
||||
"place_id": "ChIJL8eKojLGwoAR-inFlT4uF8Q",
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAAPj4xP0HMJoDhx-P7C2JZTRSxu44XKoALQqupq2WaiqDjqiqi7g1ua7imgENWEH_fXu8XS5XxAmpkYV4XcDWfbLeZFuOKbgNGIvxQhXBhL-215hJQvoHFodeO9_lCGliNEhC6QZwCTKktRp33eibHgQJyGhRZf9Z_PHxTO-dxhjCFmIY8KQUYlQ",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "2135 Sunset Blvd, Los Angeles, CA 90026, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0776068,
|
||||
"lng": -118.2646526
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.07886997989272,
|
||||
"lng": -118.2632818701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.07617032010727,
|
||||
"lng": -118.2659815298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "946525c8b38a73842b88f67b1786746dce63fc27",
|
||||
"name": "Elf Cafe",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 1365,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/104111246635874032234/photos\">ZAGAT</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAXeyh-4q2GinyRWob20cRuMTPB9uwOMr4jceLbiGh1sUI3I3dU2tqPBg0fIALpeBD4h5cfCTyULBTj6vSqNEcDUcPGFnjOaDO3BYQALO94_WCtbjkY83fQkdM_BPFlm2oEhDshmrB_41OPbyEVGJ4fPUVGhSYvebsAZyOFjSgUoJm_QQAhDFhzA",
|
||||
"width": 2048
|
||||
}],
|
||||
"place_id": "ChIJEy4mphDHwoARejZ9zUN_d4U",
|
||||
"price_level": 2,
|
||||
"rating": 4.7,
|
||||
"reference": "CmRbAAAAwxF-jK7VtlDfSJigYrgWtGrgNXRvw-4pU62YULylvTajhxW9lwzvmYKmKKx4ZO0SBMR1TOqVE-rPlhlh7aZI5NfcL8RItn_IX1BxS7X9X2ode26MDbVwICFFC_qziC-MEhA4i-3OikQ9eHbTeu-L27G1GhRLgk421tk5hnWDbhrEbtK-S3Ap4A",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "4114 Santa Monica Blvd, Los Angeles, CA 90029, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0919263,
|
||||
"lng": -118.2820544
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.09333132989273,
|
||||
"lng": -118.2807559701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.09063167010728,
|
||||
"lng": -118.2834556298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "d358acb483e9bacaf445de016f78473908253696",
|
||||
"name": "Bulan Thai Vegetarian Kitchen",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3024,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/107987703028923546874/photos\">jeromy robert</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAbjH5vQk8Ese5ppONFAIwbunnNnhcEEkKtRrn5yttTYvxHxjbgbGDXIv7cNSfQMU7wICcox5NT__xnaKwJi-e3zr5230Z4t8TcReOcVoV8Ewjfn5g7AL4AK0gksctR-KqEhD6iw1PbCByU1zD7iD3lxPXGhT5AoSLQ7BHIzymU94-0xSvRMo9yA",
|
||||
"width": 4032
|
||||
}],
|
||||
"place_id": "ChIJAaqI1E7HwoARRCxBLPw048Y",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAbLDZZu-oOjRmRi7hsQpZELuusRQZEk6lE1YJL5r7pnP3RtWaSLlA_p9uYp0QIalfzXhS0605iRnNsEr_WDhos1HxubW4h0sPKz96GPUKeULOux319b2pYHqQCYwYUmOMEhCEXh-YmfKFafd1EtkAYkB2GhSbxBpYLCWz45RTP0YaWm6eeJecwQ",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "1047 S Fairfax Ave, Los Angeles, CA 90019, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0568525,
|
||||
"lng": -118.3646369
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.05817752989272,
|
||||
"lng": -118.3632100701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.05547787010728,
|
||||
"lng": -118.3659097298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "6f73d8dfd2f3dc8b08d127ea3d0691b33030973b",
|
||||
"name": "Rahel Ethiopian Vegan Cuisine",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 2988,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/100435880280656818067/photos\">Edna Banayat</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAsltyy0_FJzHaxUaRfy3ks0qgLs8N0x2Y7dZqKDw21NUi7D9STyFscfcUKiL9z9E2LXC3jrIqq6vikR6j0hb8_L7kCDr1WjgVUamyzOw3GdXSmaDVEQrOWDkBSGWGCvSAEhA9zXJDgrZrtdC90Jah1VWnGhRr8wcVDTal8O1R7S6WOQ0482phCw",
|
||||
"width": 5312
|
||||
}],
|
||||
"place_id": "ChIJqeeNwhW5woARdmN2K2Q107A",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAoNTyxGrtvDexCku_OpmL47Akpq5T587ncWV1Rp8xWIc7YQlqk75_0h2_nB0bduMRadfyHsW6i6SXFUNicU0BWvaGGy3Mtta6Omd-9lePm-ZN2EQBbuT05kFBNHkWbs9zEhCfXjvnfKzALRwRyDKEeQr-GhRIXuOSJHoM7g6L-V83WDXl3mxUgQ",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "4664, 10438 National Blvd, Los Angeles, CA 90034, United States",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0285781,
|
||||
"lng": -118.4115541
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.02999047989272,
|
||||
"lng": -118.4102540701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.02729082010728,
|
||||
"lng": -118.4129537298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "92cc1f89e314ba9e518f1d7aa345a57441affe39",
|
||||
"name": "The Vegan Joint (West LA)",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 421,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/106873412906570774753/photos\">The Vegan Joint (West LA)</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAN3v7CUQbGdsnPNSZ26qvp11iqa78bJjwe_P9pr0rA0meuKU916gHo0OktysWHWB-ZhFRtiVGYQeTm6mQUVChAxglotv9Tgv4VTRAIFdy_-WeLSvkQz-KhOf-mtvmfObfEhCOC0m29jY1Dvm0Xz_l2s1sGhT-9vWMge8CoQrb5wpFAtLrUB8phg",
|
||||
"width": 750
|
||||
}],
|
||||
"place_id": "ChIJ74X8ncu7woARzxiCmkqeeXc",
|
||||
"price_level": 2,
|
||||
"rating": 4.4,
|
||||
"reference": "CmRbAAAAh4e7HwMd83IQ1TW182b2n_qdhUDULWHH33HQjJOT_DGzaFV2HyLLEnN4z07DGn_-S0GsA2mwy3hvI-GFIKW1hM-VO8MOaJkiFMfWW347qlVy6iYmltY0DzU_-eA02rHPEhC0AGDxyCgGEH-2PrMsG0_ZGhTFDKatE5VBRZkTdWAaYfzSrs3QDg",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "3655 S Grand Ave, Los Angeles, CA 90007, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.017339,
|
||||
"lng": -118.278469
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.01859817989273,
|
||||
"lng": -118.2769120701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.01589852010728,
|
||||
"lng": -118.2796117298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "da772e983e3e6dbc0cd042042311ab79839d1f28",
|
||||
"name": "Azla Ethiopian Vegan Cuisine",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3024,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/110952242659405432450/photos\">Benjamin Rubin</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAARwfVFSLVUmk_dVkBaY43M8sPng1XdXX7Is882vzG7KcFewp2dMMlYqwKQSGyfAOSWZ3vJrgBUEypUwJESuqoxRneTLjp9W-XcQ4pQByDVabpYbT5r5y006esU3aTeHvIEhDXtTfa4Kh1z_-u4D_BFJT2GhSQDwL9zFfvKbwRuGmmcui08SdiLg",
|
||||
"width": 4032
|
||||
}],
|
||||
"place_id": "ChIJmb4BkgrIwoAR-PS33wrjk_M",
|
||||
"rating": 4.8,
|
||||
"reference": "CmRbAAAA5lMrGEVyK5VhjDMMJZ2tUR0Poo9zQt4wZZBcW46e3CfsYUN0INPZKeeKERZJwkhcogLK-FsKAZI92jncCYN4RZQYqtyiJONx3I6VWIrTXGI9rOt01dSTtBCZEKtag0AcEhBlaMFWX2bnU8gMjCv3Fs1AGhSs8gJhrAa-jQNLRX7pxzIqBJ_HQg",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "8101½ Beverly Blvd, Los Angeles, CA 90048, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0764288,
|
||||
"lng": -118.3661624
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.07760962989272,
|
||||
"lng": -118.3648135201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.07490997010728,
|
||||
"lng": -118.3675131798927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "1924e9b97b3101ea1d18ac33191e8c2d8fffcbc2",
|
||||
"name": "Araya's Vegetarian Place",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3088,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/103485549458724391583/photos\">Simon Catford</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAyNe-nZSiUqUrfU3RKs7KqCG2-yoYpRixjGySI1LQgkDKkmRycyaz8RNyaVTYD8bqIf8FpmH7MOvC84sF6gVTsOBKI3IUGeyx54Uxntf8IkZOx4MjDr-Gdh_e40hXYKePEhApDNZUIBhEwnQHrWJqcBA1GhSN3DbanyozQB0pl8wVQucBZbsg1g",
|
||||
"width": 4160
|
||||
}],
|
||||
"place_id": "ChIJV-aT-jS5woAROYkaRCGf2s8",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAeSSwdHRB2eK9ro7pBWjJZeiDbH12E8AhhMyUsosVZeCMtzmNgPNfRgbS96NRPgdbWrudWjLJrKcomMqCyH8PWpA8rhSKHf7Kj7u7rgBv0r3b4kowdzJ4VUe8WyNoGQ_VEhBVcix6m4C5QlEf-G3y7AveGhTqHZ55gdeHglCukJFsCIpkzMR9Cw",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "4507 S Centinela Ave, Los Angeles, CA 90066, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 33.9925942,
|
||||
"lng": -118.4232475
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 33.99401072989273,
|
||||
"lng": -118.4218230701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 33.99131107010729,
|
||||
"lng": -118.4245227298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "8f5ac5552a0e53f7ae627916c3d1d48b45030f54",
|
||||
"name": "LA Vegan",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3644,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/108088361466169015751/photos\">Eric Estenzo</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAdcatw_mFed_48beFNIsyCrjLRenujvkj0YkhN5PX8oJQAr013xQEGat49VQ1GmrvEMRTV8CIeWtBOYSwzIWUrD7kyY1c3sgmx-0y_ecKUIuSlxhlbDtjQeCa3U8SV5yGEhAGwkRlZ2kuFsZ5uBKLh79CGhRKQDVIo1Z_ijdaHPLZqjuRU8mcew",
|
||||
"width": 7012
|
||||
}],
|
||||
"place_id": "ChIJZUzGG266woARXsRdJc2silw",
|
||||
"price_level": 2,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAXBx1MbFAy5X5HJqg1yMWtv10pIpp5Qeq2lCD7y7dhJM5B8iHXgysRGXgjsD5eTeKnxuaM26srTPC3Ib7g1TfBAnaIcVVw7K0-JEiuRzs-dBnuj_1ey5A0r3zvXOQpcN0EhB18uIS3_Rj_T61b2Sfo0ecGhSmROJ3Of8T44ckuMzOqKUJsZng8Q",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "8393 Beverly Blvd, Los Angeles, CA 90048, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0764345,
|
||||
"lng": -118.3730332
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.07760002989272,
|
||||
"lng": -118.3716821701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.07490037010728,
|
||||
"lng": -118.3743818298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "1d6756ade69dac8cf5ce42aeeeb793535b4504e8",
|
||||
"name": "Vegan Glory",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 570,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/104981305421921650092/photos\">Thom Uber</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAA1YSeQpoJrDP6swpc1vw2b364pPMsjw2s2DVZYV4AWfENCwY02zHen8VpfI27Vnh8762bVFs4kda28R2sH354xKhju0vBIEM4yRlQr_G1-NvcywFtMQE7mBVtgegTo6ADEhBBLf_S0InHdmZ-SVC1KX8zGhQS9hpDvsrqVNDPNuWKLSPHy34XoA",
|
||||
"width": 760
|
||||
}],
|
||||
"place_id": "ChIJyYMRL0u5woARId-DVapUFNk",
|
||||
"rating": 4.4,
|
||||
"reference": "CmRbAAAActbey6zjHroEmUk5ZVCyD6LR0-RHIuWbMAJW5v7PaA-V-pijZFT9MgDjuoCOYAy7asUfIXfLiPxli2mdJtUGTCsUW6qw91uHaZR6XQv62bUFwmM9qM6IB6gOcv4T5BKbEhD9Jo87T4pNpyFSgCZWcyMOGhT2Au7pVgKuofn6_B7s4vSFUNhXPA",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "1253 Vine St # 9, Los Angeles, CA 90038, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.093981,
|
||||
"lng": -118.327638
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.09533472989273,
|
||||
"lng": -118.3257989701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.09263507010728,
|
||||
"lng": -118.3284986298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "a869aba570e8103a6ae09ae71335a38e99b11e01",
|
||||
"name": "Doomie's Home Cookin'",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 2400,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/114793967685216290949/photos\">Joe Gebis</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAA20MBConiFPlO2-6BuoTBSGSAUBJaYoX0_m0xj_MNE2BPaqhDDSNtUXMTcmiKKZw0umwGkTaBt-Y3kNfKwKfRAdNnW9byHMuvElqBWXZTPSPuTIN_4xJKt1tKsosLuIfBEhB_ZCYjBhFWDue7-Z-UcfFjGhRyD45yAH2035Ifu15OklnylBIfhA",
|
||||
"width": 3200
|
||||
}],
|
||||
"place_id": "ChIJ56wYlja_woAR7f2m0YbS8hk",
|
||||
"price_level": 2,
|
||||
"rating": 4.4,
|
||||
"reference": "CmRbAAAAObVl81DX-3CYhyQv0itdfPojjR7bwG0eAv2JpZb9ZYoVBNg9F0znMXC72bBsDctx7ogiPKPvagqMNtEFkHYG7aLMk5VO-xtR8sfDAgeEiHHZV24oStUWs4aY-xqrU8W4EhAok2aSJPg0eRFD7nXeJpzzGhRPGtkJYZeCZHvy9UctDGItmFdbKg",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "710 W 1st St, Los Angeles, CA 90012, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.056385,
|
||||
"lng": -118.2508724
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.05794337989273,
|
||||
"lng": -118.2493326201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.05524372010728,
|
||||
"lng": -118.2520322798927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "53b4341b80e3596c05431abaa9b42b3fb0adf66d",
|
||||
"name": "Âu Lạc LA",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 3036,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/103902774314509444130/photos\">Patrick Donnelly</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAmofVPSjKMklO3J2mpcRl_kHAwl710EqByuQjaP8rjOtmj08yJKKr1xcJICtaZ-quKiiG2aYUhFru5JF1eYeDHx-pILIPOmKspUN07oGjhMwKnUa73wikXvM-FVJuJcRMEhDLVub-Rx6D4N3iiN0JAtHhGhQQ2Un8dIsylPeLN9IcRcerUif-1w",
|
||||
"width": 4048
|
||||
}],
|
||||
"place_id": "ChIJe0sPnFLGwoARxFDUYFEjhuQ",
|
||||
"price_level": 2,
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAAHb0M5CcLv12TZJiEq0QRs5nexZ-fBdKvzQmTE8w3j_-YHX15hSy7WYb3EuweeyxhuIBirsVr4uBMtSUP4KoClwz1l6uE0KgO5jHySb6a9Qi5_Hoju3iN55xLA38dCGPTEhBqVAmmstFHL-wzx6LKFLkWGhTdLVJpc_s2vuSY-OL3rJzZStmuFg",
|
||||
"types": [
|
||||
"bar",
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "2870 Rowena Ave, Los Angeles, CA 90039, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.107701,
|
||||
"lng": -118.2667943
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.10916667989272,
|
||||
"lng": -118.2653993701072
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.10646702010727,
|
||||
"lng": -118.2680990298927
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "199ee04e9dbcb33b16d3aed792b6c0cc0e441d8d",
|
||||
"name": "Little Pine",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 563,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/113035519513505430750/photos\">Karen TenEyck</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAoYVVT_rhyzFXzPVGgWSkRNXU5Fqm7AC5BcX7obilkGQX_cqVfu74Jb4Dm77gWDtvF3uov5Oeu06JutufKGQwbWlBJmdH0O3xCDoNA-t5uPOHjvZu26A2WYA3JtaMR4TzEhAbM1Fsz-8UUaZOh37fbEs3GhRdnpshI2u0QbwbL0KbAa_IiNPPdg",
|
||||
"width": 1000
|
||||
}],
|
||||
"place_id": "ChIJjc_P0c3AwoARBWVMPN_YVRU",
|
||||
"price_level": 2,
|
||||
"rating": 4.7,
|
||||
"reference": "CmRbAAAAugyZhEPl2BK5sW7_AnhEZv-d-21pR6GzooTVD3RJFsDF9hecZMusog36IO34t7wuFczZWyD8kEiVKilj4Sp9buBdtO077raxcOkVC6--BXKuhGrh2Lkq5dhR2pLXNLXZEhBKcQzGE58Xr82Fz7Oy--MaGhTT7qBjkenQ3jVinJpgqASlYR5c0g",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "Little Tokyo Shopping Center, 333 South Alameda St #310, Los Angeles, CA 90013, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0450139,
|
||||
"lng": -118.2388682
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.04636372989273,
|
||||
"lng": -118.2375183701073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.04366407010728,
|
||||
"lng": -118.2402180298928
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "bdc0c9353ceb91395214f3f84f0016059271acd7",
|
||||
"name": "Shojin",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 2336,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/100540448898812651487/photos\">Roman Roze</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAALLVT3JKv4U1gRmbItfHhzOIdYRIibKmLMWThQa0gYU-umKbevliJUIogQL_Hm1_6oWGlutmR4kuzd6ikrpM4_eIeLSprpxAlHiEmi3FVNN5XvnBxfV8xWj6ggQLNM8DxEhDscXYyU5r1aKlYTCVLG61tGhQsd9XLF9OR7UL9XZo_eOm1Y98hCw",
|
||||
"width": 3504
|
||||
}],
|
||||
"place_id": "ChIJyy_0pjnGwoARi47oqNbdedU",
|
||||
"price_level": 3,
|
||||
"rating": 4.6,
|
||||
"reference": "CmRbAAAAORx-wW_TP5eWz0P93UBThqK-FdOLxos-ptjGRGlmiFaOf6ySc43rGF1ENCNilSFQL0Q-7RtFawCJMdvApBCTEyHZnRIIx4Jp4cRIcvSxRBJzB-S3aWfAglqv4L_sO7zsEhAjm-hxEpM5a-8fqVVcqJAbGhQDjg_IeMxUnePapODPTZPxOis_Lw",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "2520 Glendale Blvd, Los Angeles, CA 90039, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.1031997,
|
||||
"lng": -118.2586152
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.10455672989272,
|
||||
"lng": -118.2575728201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.10185707010727,
|
||||
"lng": -118.2602724798928
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "52be338abc98361f3c8664a7d4e853a11332afec",
|
||||
"name": "by CHLOE. Silver Lake",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 2988,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/104636687373132435944/photos\">Jeannette G.</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAA4Yb-teP-cLR0wHYMwtNvoo-wPlgVsj444v-VTgdwi63Gshd-PV5ZMhVPQSN2v5yPM-lxvZQW7Redm4wNKPLmGku5MNs0yhMuBysvXO6gpJ6kF4PGCepMIcZQ409LpBD_EhD8ZeAMspzsx_RnDHSQkuDlGhQ3-VOGj2K85VfvxsOCAyIS4HdVnw",
|
||||
"width": 5312
|
||||
}],
|
||||
"place_id": "ChIJ4fYRfivHwoAR_2DlpZxO5_8",
|
||||
"price_level": 2,
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAALsJqmhwH-aLZPy6SNAbLNzQRQ3qfT92yX3k6t13Ynq9K5hQatkTEBPhTr-pqcF3RznWRRYDNUoyPSoT8irbse6j2VZUHsgvo7wvPPVSTeYaeB3gZYXNaFnJmcqyUUU4CEhAhjByzVT-NNxUGH3pcyuS-GhTSUc8pvZUzjDie_3LXHrkObrtDzA",
|
||||
"types": [
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"formatted_address": "639 N Larchmont Blvd, Los Angeles, CA 90004, USA",
|
||||
"geometry": {
|
||||
"location": {
|
||||
"lat": 34.0828183,
|
||||
"lng": -118.3241586
|
||||
},
|
||||
"viewport": {
|
||||
"northeast": {
|
||||
"lat": 34.08422687989272,
|
||||
"lng": -118.3225729201073
|
||||
},
|
||||
"southwest": {
|
||||
"lat": 34.08152722010728,
|
||||
"lng": -118.3252725798928
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
|
||||
"id": "21f5bb524cea1c8698187b817298300df290ad1b",
|
||||
"name": "Cafe Gratitude",
|
||||
"opening_hours": {
|
||||
"open_now": false,
|
||||
"weekday_text": []
|
||||
},
|
||||
"photos": [{
|
||||
"height": 1365,
|
||||
"html_attributions": [
|
||||
"<a href=\"https://maps.google.com/maps/contrib/115181038312887901832/photos\">Cafe Gratitude</a>"
|
||||
],
|
||||
"photo_reference": "CmRaAAAAX_eBNo4lUv8qGy5th3upd5Qp_Gx9NEQ55wfy0Yb7l1L_0M_gIwQqPb55RiJivmK_Wu3_CiyjBwEVGeeQCGRPHanVPYIG7_fQGV3_o2QgPSgQOAEfeKvuvdeUFGLX2TMzEhANtSJNyyzGbezwvsEmgvklGhSNKWwiEgOHaw28SqiWmqm1aAWDJQ",
|
||||
"width": 2048
|
||||
}],
|
||||
"place_id": "ChIJjRPL7sm4woARFcIwlfejAGA",
|
||||
"price_level": 2,
|
||||
"rating": 4.5,
|
||||
"reference": "CmRbAAAANXEcxEtfEYz2V5OIwdFEuyw35xZZ7SpF9AVuYFUqha3tf8CiZbuYrXLCG_y-twfXpcRiYyN0kXDfwmjA-uPZQaPbfpriBY397myHuiFF1A1ESI-5Hcb1y6UiRger5g3tEhCRq3Ml_vdNqA8AGua9iLXrGhTz1KC9902YirqAu7KLW4Bq7FYOEQ",
|
||||
"types": [
|
||||
"cafe",
|
||||
"restaurant",
|
||||
"food",
|
||||
"point_of_interest",
|
||||
"establishment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "OK"
|
||||
}
|
||||
48
example/src/App.js
Normal file
48
example/src/App.js
Normal file
@ -0,0 +1,48 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import GoogleMapReact from 'google-map-react'
|
||||
// import 'google-map-react/dist/index.css'
|
||||
|
||||
import LOS_ANGELES_CENTER from './const/la_center';
|
||||
|
||||
import Marker from './components/Marker';
|
||||
|
||||
const Wrapper = styled.main`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const [places, setPlaces] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetch('places.json')
|
||||
.then((response) => response.json())
|
||||
.then((data) => setPlaces(data.results))
|
||||
})
|
||||
|
||||
if (!places || places.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<GoogleMapReact
|
||||
defaultZoom={10}
|
||||
defaultCenter={LOS_ANGELES_CENTER}
|
||||
>
|
||||
{places.map((place) => (
|
||||
<Marker
|
||||
key={place.id}
|
||||
text={place.name}
|
||||
lat={place.geometry.location.lat}
|
||||
lng={place.geometry.location.lng}
|
||||
/>
|
||||
))}
|
||||
</GoogleMapReact>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
9
example/src/App.test.js
Normal file
9
example/src/App.test.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div')
|
||||
ReactDOM.render(<App />, div)
|
||||
ReactDOM.unmountComponentAtNode(div)
|
||||
})
|
||||
38
example/src/components/Marker.js
Normal file
38
example/src/components/Marker.js
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #000;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 100%;
|
||||
user-select: none;
|
||||
transform: translate(-50%, -50%);
|
||||
cursor: ${(props) => (props.onClick ? 'pointer' : 'default')};
|
||||
&:hover {
|
||||
z-index: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
const Marker = ({ text, onClick }) => (
|
||||
<Wrapper
|
||||
alt={text}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
|
||||
Marker.defaultProps = {
|
||||
onClick: null,
|
||||
};
|
||||
|
||||
Marker.propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
text: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Marker;
|
||||
1
example/src/const/la_center.js
Normal file
1
example/src/const/la_center.js
Normal file
@ -0,0 +1 @@
|
||||
export default [34.0522, -118.2437];
|
||||
19
example/src/index.css
Normal file
19
example/src/index.css
Normal file
@ -0,0 +1,19 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
/* this allows map to extend through the height of the dom */
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
}
|
||||
7
example/src/index.js
Normal file
7
example/src/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import './index.css'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
10805
example/yarn.lock
Normal file
10805
example/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
147
package.json
147
package.json
@ -1,28 +1,67 @@
|
||||
{
|
||||
"name": "google-map-react",
|
||||
"version": "1.1.7",
|
||||
"description": "isomorphic google map react component, allows render react components on the google map",
|
||||
"main": "lib/index",
|
||||
"scripts": {
|
||||
"build:lib": "babel ./src -d lib",
|
||||
"build:umd": "webpack src/index_umd.js dist/GoogleMapReact.js --config webpack.config.dev.js",
|
||||
"build:umd:min": "webpack src/index_umd.js dist/GoogleMapReact.min.js --config webpack.config.prod.js",
|
||||
"build": "yarn run build:lib && yarn run build:umd && yarn run build:umd:min",
|
||||
"clean": "rimraf lib dist",
|
||||
"prepublish": "yarn run clean && yarn run build",
|
||||
"format": "prettier --trailing-comma es5 --single-quote --write 'src/**/*.js' 'src/**/*.js'",
|
||||
"format:dev": "prettier --trailing-comma es5 --single-quote --write 'develop/**/*.js' 'develop/**/*.js'",
|
||||
"lint": "eslint src",
|
||||
"lint:dev": "eslint develop",
|
||||
"test": "NODE_ENV=eee mocha --compilers js:babel-register --recursive --require babel-polyfill",
|
||||
"test:watch": "NODE_ENV=eee mocha --compilers js:babel-register --recursive --require babel-polyfill --watch",
|
||||
"start": "kotatsu serve --port 4000 --config ./develop/config/index.babel.js --presets es2015,stage-0,react,react-hmre ./develop/Main.js",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"description": "Isomorphic component that allows rendering react components on a google map",
|
||||
"author": "istarkov https://github.com/istarkov",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/google-map-react/google-map-react.git"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.modern.js",
|
||||
"source": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "microbundle-crl --no-compress --format modern,cjs",
|
||||
"start": "microbundle-crl watch --no-compress --format modern,cjs",
|
||||
"prepare": "run-s build",
|
||||
"test": "run-s test:unit test:lint test:build",
|
||||
"test:build": "run-s build",
|
||||
"test:lint": "eslint .",
|
||||
"test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
|
||||
"test:watch": "react-scripts test --env=jsdom",
|
||||
"predeploy": "cd example && yarn install && yarn run build",
|
||||
"deploy": "gh-pages -d example/build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"eventemitter3": "^4.0.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"scriptjs": "^2.5.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.0.3",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"eslint-config-standard": "^14.1.0",
|
||||
"eslint-config-standard-react": "^9.2.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.17.0",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"expect": "1.20.2",
|
||||
"jsdom": "^6.5.1",
|
||||
"gh-pages": "^2.2.0",
|
||||
"microbundle-crl": "^0.13.10",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "^3.4.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [
|
||||
"react",
|
||||
"reactjs",
|
||||
@ -34,73 +73,5 @@
|
||||
"component",
|
||||
"javascript",
|
||||
"react-component"
|
||||
],
|
||||
"author": "istarkov https://github.com/istarkov",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/google-map-react/google-map-react/issues"
|
||||
},
|
||||
"homepage": "https://github.com/google-map-react/google-map-react#readme",
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
"eventemitter3": "^4.0.4",
|
||||
"prop-types": "^15.5.6",
|
||||
"scriptjs": "^2.5.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.3.6",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "^7.2.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-webpack-loaders": "^0.5.0",
|
||||
"babel-polyfill": "^6.13.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"babel-preset-react-hmre": "^1.1.1",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"babel-register": "^6.14.0",
|
||||
"css-loader": "^0.23.1",
|
||||
"eslint": "^4.18.2",
|
||||
"eslint-config-airbnb": "^14.0.0",
|
||||
"eslint-config-prettier": "^1.5.0",
|
||||
"eslint-plugin-babel": "^4.1.1",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^4.0.0",
|
||||
"eslint-plugin-prettier": "^2.0.1",
|
||||
"eslint-plugin-react": "^6.8.0",
|
||||
"expect": "^1.11.1",
|
||||
"file-loader": "^0.8.5",
|
||||
"husky": "^0.13.3",
|
||||
"jsdom": "^6.5.1",
|
||||
"kotatsu": "^0.14.0",
|
||||
"lint-staged": "^3.4.0",
|
||||
"mocha": "^5.2.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"normalize.css": "^4.1.1",
|
||||
"postcss-loader": "^0.9.1",
|
||||
"prettier": "^0.22.0",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-motion": "^0.4.4",
|
||||
"react-router": "^3.2.0",
|
||||
"recompose": "^0.26.0",
|
||||
"reselect": "^2.5.1",
|
||||
"rimraf": "^2.4.3",
|
||||
"rxjs": "^5.0.0-beta.8",
|
||||
"sass-loader": "^4.1.1",
|
||||
"style-loader": "^0.13.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.12.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --trailing-comma es5 --single-quote --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,16 +1,13 @@
|
||||
export const generateHeatmap = (instance, { positions }) =>
|
||||
new instance.visualization.HeatmapLayer({
|
||||
data: positions.reduce(
|
||||
(acc, { lat, lng, weight = 1 }) => {
|
||||
acc.push({
|
||||
location: new instance.LatLng(lat, lng),
|
||||
weight,
|
||||
});
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
),
|
||||
data: positions.reduce((acc, { lat, lng, weight = 1 }) => {
|
||||
acc.push({
|
||||
location: new instance.LatLng(lat, lng),
|
||||
weight,
|
||||
});
|
||||
return acc;
|
||||
}, []),
|
||||
});
|
||||
|
||||
export const optionsHeatmap = (instance, { options = {} }) =>
|
||||
Object.keys(options).map(option => instance.set(option, options[option]));
|
||||
Object.keys(options).map((option) => instance.set(option, options[option]));
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies, react/forbid-prop-types, react/no-find-dom-node, no-console */
|
||||
/* eslint-disable import/no-extraneous-dependencies, react/forbid-prop-types, react/no-find-dom-node, no-console, no-undef */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactDOM from 'react-dom';
|
||||
@ -13,19 +13,24 @@ import { generateHeatmap, optionsHeatmap } from './google_heatmap';
|
||||
// loaders
|
||||
import googleMapLoader from './loaders/google_map_loader';
|
||||
|
||||
// utils
|
||||
import Geo from './utils/geo';
|
||||
// lib
|
||||
import Geo from './lib/geo';
|
||||
|
||||
// tools
|
||||
import raf from './utils/raf';
|
||||
import pick from './utils/pick';
|
||||
import log2 from './utils/log2';
|
||||
import omit from './utils/omit';
|
||||
import log2 from './utils/math/log2';
|
||||
import pick from './utils/pick';
|
||||
import isEmpty from './utils/isEmpty';
|
||||
import isNumber from './utils/isNumber';
|
||||
import detectBrowser from './utils/detect';
|
||||
import shallowEqual from './utils/shallowEqual';
|
||||
import isPlainObject from './utils/isPlainObject';
|
||||
import isArraysEqualEps from './utils/isArraysEqualEps';
|
||||
import detectElementResize from './utils/detectElementResize';
|
||||
import {
|
||||
addResizeListener,
|
||||
removeResizeListener,
|
||||
} from './utils/detectElementResize';
|
||||
import addPassiveEventListener from './utils/passiveEvents';
|
||||
|
||||
// consts
|
||||
@ -62,7 +67,7 @@ function defaultOptions_(/* maps */) {
|
||||
};
|
||||
}
|
||||
|
||||
const latLng2Obj = latLng =>
|
||||
const latLng2Obj = (latLng) =>
|
||||
isPlainObject(latLng) ? latLng : { lat: latLng[0], lng: latLng[1] };
|
||||
|
||||
const _checkMinZoom = (zoom, minZoom) => {
|
||||
@ -292,7 +297,7 @@ export default class GoogleMap extends Component {
|
||||
);
|
||||
if (this.props.resetBoundsOnResize) {
|
||||
const that = this;
|
||||
detectElementResize.addResizeListener(mapDom, that._mapDomResizeCallback);
|
||||
addResizeListener(mapDom, that._mapDomResizeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,9 +372,10 @@ export default class GoogleMap extends Component {
|
||||
!shallowEqual(this.props.options, nextProps.options)
|
||||
) {
|
||||
const mapPlainObjects = pick(this.maps_, isPlainObject);
|
||||
let options = typeof nextProps.options === 'function'
|
||||
? nextProps.options(mapPlainObjects)
|
||||
: nextProps.options;
|
||||
let options =
|
||||
typeof nextProps.options === 'function'
|
||||
? nextProps.options(mapPlainObjects)
|
||||
: nextProps.options;
|
||||
// remove zoom, center and draggable options as these are managed by google-maps-react
|
||||
options = omit(options, ['zoom', 'center', 'draggable']);
|
||||
|
||||
@ -382,7 +388,7 @@ export default class GoogleMap extends Component {
|
||||
}
|
||||
|
||||
if (!shallowEqual(nextProps.layerTypes, this.props.layerTypes)) {
|
||||
Object.keys(this.layers_).forEach(layerKey => {
|
||||
Object.keys(this.layers_).forEach((layerKey) => {
|
||||
this.layers_[layerKey].setMap(null);
|
||||
delete this.layers_[layerKey];
|
||||
});
|
||||
@ -394,7 +400,7 @@ export default class GoogleMap extends Component {
|
||||
!shallowEqual(nextProps.heatmap.positions, this.props.heatmap.positions)
|
||||
) {
|
||||
this.heatmap.setData(
|
||||
nextProps.heatmap.positions.map(p => ({
|
||||
nextProps.heatmap.positions.map((p) => ({
|
||||
location: new this.maps_.LatLng(p.lat, p.lng),
|
||||
weight: p.weight,
|
||||
}))
|
||||
@ -405,10 +411,12 @@ export default class GoogleMap extends Component {
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
// draggable does not affect inner components
|
||||
return !shallowEqual(
|
||||
omit(this.props, ['draggable']),
|
||||
omit(nextProps, ['draggable'])
|
||||
) || !shallowEqual(this.state, nextState);
|
||||
return (
|
||||
!shallowEqual(
|
||||
omit(this.props, ['draggable']),
|
||||
omit(nextProps, ['draggable'])
|
||||
) || !shallowEqual(this.state, nextState)
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -429,10 +437,7 @@ export default class GoogleMap extends Component {
|
||||
window.removeEventListener('keydown', this._onKeyDownCapture);
|
||||
window.removeEventListener('mouseup', this._onChildMouseUp, false);
|
||||
if (this.props.resetBoundsOnResize) {
|
||||
detectElementResize.removeResizeListener(
|
||||
mapDom,
|
||||
this._mapDomResizeCallback
|
||||
);
|
||||
removeResizeListener(mapDom, this._mapDomResizeCallback);
|
||||
}
|
||||
|
||||
if (this.overlay_) {
|
||||
@ -459,25 +464,24 @@ export default class GoogleMap extends Component {
|
||||
delete this.markersDispatcher_;
|
||||
}
|
||||
}
|
||||
|
||||
// calc minZoom if map size available
|
||||
// it's better to not set minZoom less than this calculation gives
|
||||
// otherwise there is no homeomorphism between screen coordinates and map
|
||||
// (one map coordinate can have different screen coordinates)
|
||||
_getMinZoom = () => {
|
||||
if (this.geoService_.getWidth() > 0 || this.geoService_.getHeight() > 0) {
|
||||
const tilesPerWidth = Math.ceil(
|
||||
this.geoService_.getWidth() / K_GOOGLE_TILE_SIZE
|
||||
) + 2;
|
||||
const tilesPerHeight = Math.ceil(
|
||||
this.geoService_.getHeight() / K_GOOGLE_TILE_SIZE
|
||||
) + 2;
|
||||
const tilesPerWidth =
|
||||
Math.ceil(this.geoService_.getWidth() / K_GOOGLE_TILE_SIZE) + 2;
|
||||
const tilesPerHeight =
|
||||
Math.ceil(this.geoService_.getHeight() / K_GOOGLE_TILE_SIZE) + 2;
|
||||
const maxTilesPerDim = Math.max(tilesPerWidth, tilesPerHeight);
|
||||
return Math.ceil(log2(maxTilesPerDim));
|
||||
}
|
||||
return DEFAULT_MIN_ZOOM;
|
||||
};
|
||||
|
||||
_computeMinZoom = minZoom => {
|
||||
_computeMinZoom = (minZoom) => {
|
||||
if (!isEmpty(minZoom)) {
|
||||
return minZoom;
|
||||
}
|
||||
@ -496,8 +500,8 @@ export default class GoogleMap extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_setLayers = layerTypes => {
|
||||
layerTypes.forEach(layerType => {
|
||||
_setLayers = (layerTypes) => {
|
||||
layerTypes.forEach((layerType) => {
|
||||
this.layers_[layerType] = new this.maps_[layerType]();
|
||||
this.layers_[layerType].setMap(this.map_);
|
||||
});
|
||||
@ -543,7 +547,7 @@ export default class GoogleMap extends Component {
|
||||
|
||||
this.props
|
||||
.googleMapLoader(bootstrapURLKeys, this.props.heatmapLibrary)
|
||||
.then(maps => {
|
||||
.then((maps) => {
|
||||
if (!this.mounted_) {
|
||||
return;
|
||||
}
|
||||
@ -576,9 +580,10 @@ export default class GoogleMap extends Component {
|
||||
// "MaxZoomStatus", "StreetViewStatus", "TransitMode", "TransitRoutePreference",
|
||||
// "TravelMode", "UnitSystem"
|
||||
const mapPlainObjects = pick(maps, isPlainObject);
|
||||
const options = typeof this.props.options === 'function'
|
||||
? this.props.options(mapPlainObjects)
|
||||
: this.props.options;
|
||||
const options =
|
||||
typeof this.props.options === 'function'
|
||||
? this.props.options(mapPlainObjects)
|
||||
: this.props.options;
|
||||
const defaultOptions = defaultOptions_(mapPlainObjects);
|
||||
|
||||
const draggableOptions = !isEmpty(this.props.draggable) && {
|
||||
@ -625,12 +630,10 @@ export default class GoogleMap extends Component {
|
||||
const this_ = this;
|
||||
const overlay = Object.assign(new maps.OverlayView(), {
|
||||
onAdd() {
|
||||
const K_MAX_WIDTH = typeof screen !== 'undefined'
|
||||
? `${screen.width}px`
|
||||
: '2000px';
|
||||
const K_MAX_HEIGHT = typeof screen !== 'undefined'
|
||||
? `${screen.height}px`
|
||||
: '2000px';
|
||||
const K_MAX_WIDTH =
|
||||
typeof screen !== 'undefined' ? `${screen.width}px` : '2000px';
|
||||
const K_MAX_HEIGHT =
|
||||
typeof screen !== 'undefined' ? `${screen.height}px` : '2000px';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.style.backgroundColor = 'transparent';
|
||||
@ -643,7 +646,7 @@ export default class GoogleMap extends Component {
|
||||
if (this_.props.overlayViewDivStyle) {
|
||||
const { overlayViewDivStyle } = this_.props;
|
||||
if (typeof overlayViewDivStyle === 'object') {
|
||||
Object.keys(overlayViewDivStyle).forEach(property => {
|
||||
Object.keys(overlayViewDivStyle).forEach((property) => {
|
||||
div.style[property] = overlayViewDivStyle[property];
|
||||
});
|
||||
}
|
||||
@ -732,7 +735,8 @@ export default class GoogleMap extends Component {
|
||||
const TIMEOUT_ZOOM = 300;
|
||||
|
||||
if (
|
||||
new Date().getTime() - this.zoomControlClickTime_ < TIMEOUT_ZOOM
|
||||
new Date().getTime() - this.zoomControlClickTime_ <
|
||||
TIMEOUT_ZOOM
|
||||
) {
|
||||
// there is strange Google Map Api behavior in chrome when zoom animation of map
|
||||
// is started only on second raf call, if was click on zoom control
|
||||
@ -744,7 +748,8 @@ export default class GoogleMap extends Component {
|
||||
raf(() => {
|
||||
this_.updateCounter_++;
|
||||
this_._onBoundsChanged(map, maps);
|
||||
}));
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this_.updateCounter_++;
|
||||
this_._onBoundsChanged(map, maps);
|
||||
@ -821,7 +826,7 @@ export default class GoogleMap extends Component {
|
||||
this_._onMapTypeIdChange(map.getMapTypeId());
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
// notify callback of load failure
|
||||
this._onGoogleApiLoaded({
|
||||
map: null,
|
||||
@ -937,7 +942,7 @@ export default class GoogleMap extends Component {
|
||||
this.resetSizeOnIdle_ = true;
|
||||
};
|
||||
|
||||
_onMapMouseMove = e => {
|
||||
_onMapMouseMove = (e) => {
|
||||
if (!this.mouseInMap_) return;
|
||||
|
||||
const currTime = new Date().getTime();
|
||||
@ -980,7 +985,7 @@ export default class GoogleMap extends Component {
|
||||
this.dragTime_ === 0 &&
|
||||
this.props.onClick(...args);
|
||||
|
||||
_onMapClick = event => {
|
||||
_onMapClick = (event) => {
|
||||
if (this.markersDispatcher_) {
|
||||
// support touch events and recalculate mouse position on click
|
||||
this._onMapMouseMove(event);
|
||||
@ -1000,13 +1005,13 @@ export default class GoogleMap extends Component {
|
||||
|
||||
// gmap can't prevent map drag if mousedown event already occured
|
||||
// the only workaround I find is prevent mousedown native browser event
|
||||
_onMapMouseDownNative = event => {
|
||||
_onMapMouseDownNative = (event) => {
|
||||
if (!this.mouseInMap_) return;
|
||||
|
||||
this._onMapMouseDown(event);
|
||||
};
|
||||
|
||||
_onMapMouseDown = event => {
|
||||
_onMapMouseDown = (event) => {
|
||||
if (this.markersDispatcher_) {
|
||||
const currTime = new Date().getTime();
|
||||
if (currTime - this.dragTime_ > K_IDLE_TIMEOUT) {
|
||||
@ -1031,7 +1036,7 @@ export default class GoogleMap extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_isCenterDefined = center =>
|
||||
_isCenterDefined = (center) =>
|
||||
center &&
|
||||
((isPlainObject(center) && isNumber(center.lat) && isNumber(center.lng)) ||
|
||||
(center.length === 2 && isNumber(center[0]) && isNumber(center[1])));
|
||||
@ -1123,26 +1128,26 @@ export default class GoogleMap extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_registerChild = ref => {
|
||||
_registerChild = (ref) => {
|
||||
this.googleMapDom_ = ref;
|
||||
};
|
||||
|
||||
render() {
|
||||
const overlay = this.state.overlay;
|
||||
const mapMarkerPrerender = !overlay
|
||||
? <GoogleMapMarkersPrerender
|
||||
experimental={this.props.experimental}
|
||||
onChildClick={this._onChildClick}
|
||||
onChildMouseDown={this._onChildMouseDown}
|
||||
onChildMouseEnter={this._onChildMouseEnter}
|
||||
onChildMouseLeave={this._onChildMouseLeave}
|
||||
geoService={this.geoService_}
|
||||
insideMapPanes={false}
|
||||
distanceToMouse={this.props.distanceToMouse}
|
||||
getHoverDistance={this._getHoverDistance}
|
||||
dispatcher={this.markersDispatcher_}
|
||||
/>
|
||||
: null;
|
||||
const mapMarkerPrerender = !overlay ? (
|
||||
<GoogleMapMarkersPrerender
|
||||
experimental={this.props.experimental}
|
||||
onChildClick={this._onChildClick}
|
||||
onChildMouseDown={this._onChildMouseDown}
|
||||
onChildMouseEnter={this._onChildMouseEnter}
|
||||
onChildMouseLeave={this._onChildMouseLeave}
|
||||
geoService={this.geoService_}
|
||||
insideMapPanes={false}
|
||||
distanceToMouse={this.props.distanceToMouse}
|
||||
getHoverDistance={this._getHoverDistance}
|
||||
dispatcher={this.markersDispatcher_}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -66,15 +66,19 @@ export default class GoogleMapMarkers extends Component {
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (this.props.experimental === true) {
|
||||
return !shallowEqual(this.props, nextProps) ||
|
||||
return (
|
||||
!shallowEqual(this.props, nextProps) ||
|
||||
!shallowEqual(
|
||||
omit(this.state, ['hoverKey']),
|
||||
omit(nextState, ['hoverKey'])
|
||||
);
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return !shallowEqual(this.props, nextProps) ||
|
||||
!shallowEqual(this.state, nextState);
|
||||
return (
|
||||
!shallowEqual(this.props, nextProps) ||
|
||||
!shallowEqual(this.state, nextState)
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -165,7 +169,7 @@ export default class GoogleMapMarkers extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_onMouseAllow = value => {
|
||||
_onMouseAllow = (value) => {
|
||||
if (!value) {
|
||||
this._onChildMouseLeave();
|
||||
}
|
||||
@ -201,9 +205,10 @@ export default class GoogleMapMarkers extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const childKey = child.key !== undefined && child.key !== null
|
||||
? child.key
|
||||
: childIndex;
|
||||
const childKey =
|
||||
child.key !== undefined && child.key !== null
|
||||
? child.key
|
||||
: childIndex;
|
||||
const dist = this.props.distanceToMouse(
|
||||
this.dimensionsCache_[childKey],
|
||||
mp,
|
||||
@ -236,7 +241,7 @@ export default class GoogleMapMarkers extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_getDimensions = key => {
|
||||
_getDimensions = (key) => {
|
||||
const childKey = key;
|
||||
return this.dimensionsCache_[childKey];
|
||||
};
|
||||
@ -261,9 +266,10 @@ export default class GoogleMapMarkers extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
const latLng = child.props.latLng !== undefined
|
||||
? child.props.latLng
|
||||
: { lat: child.props.lat, lng: child.props.lng };
|
||||
const latLng =
|
||||
child.props.latLng !== undefined
|
||||
? child.props.latLng
|
||||
: { lat: child.props.lat, lng: child.props.lng };
|
||||
|
||||
const pt = this.props.insideMapPanes
|
||||
? this.props.geoService.fromLatLngToDivPixel(latLng)
|
||||
@ -281,9 +287,10 @@ export default class GoogleMapMarkers extends Component {
|
||||
child.props.seLatLng !== undefined ||
|
||||
(child.props.seLat !== undefined && child.props.seLng !== undefined)
|
||||
) {
|
||||
const seLatLng = child.props.seLatLng !== undefined
|
||||
? child.props.seLatLng
|
||||
: { lat: child.props.seLat, lng: child.props.seLng };
|
||||
const seLatLng =
|
||||
child.props.seLatLng !== undefined
|
||||
? child.props.seLatLng
|
||||
: { lat: child.props.seLat, lng: child.props.seLng };
|
||||
|
||||
const sePt = this.props.insideMapPanes
|
||||
? this.props.geoService.fromLatLngToDivPixel(seLatLng)
|
||||
@ -299,9 +306,10 @@ export default class GoogleMapMarkers extends Component {
|
||||
|
||||
// to prevent rerender on child element i need to pass
|
||||
// const params $getDimensions and $dimensionKey instead of dimension object
|
||||
const childKey = child.key !== undefined && child.key !== null
|
||||
? child.key
|
||||
: childIndex;
|
||||
const childKey =
|
||||
child.key !== undefined && child.key !== null
|
||||
? child.key
|
||||
: childIndex;
|
||||
|
||||
this.dimensionsCache_[childKey] = {
|
||||
x: containerPt.x,
|
||||
@ -328,10 +336,6 @@ export default class GoogleMapMarkers extends Component {
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={mainElementStyle}>
|
||||
{markers}
|
||||
</div>
|
||||
);
|
||||
return <div style={mainElementStyle}>{markers}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ const style = {
|
||||
// opacity: 0.3
|
||||
};
|
||||
|
||||
export default function(props) {
|
||||
export default function (props) {
|
||||
return (
|
||||
<div style={style}>
|
||||
<GoogleMapMarkers {...props} prerender />
|
||||
|
||||
@ -1 +1,5 @@
|
||||
export default from './google_map';
|
||||
import GoogleMap from './google_map';
|
||||
|
||||
export * from './lib';
|
||||
|
||||
export default GoogleMap;
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
import GoogleMap from './google_map';
|
||||
|
||||
module.exports = GoogleMap;
|
||||
1
src/lib/geo/README.md
Normal file
1
src/lib/geo/README.md
Normal file
@ -0,0 +1 @@
|
||||
### The code uses mapboxgl so that coordinate transformations do not depend on the library
|
||||
@ -1,6 +1,7 @@
|
||||
import Point from '@mapbox/point-geometry';
|
||||
import LatLng from './lib_geo/lat_lng';
|
||||
import Transform from './lib_geo/transform';
|
||||
|
||||
import LatLng from './lat_lng';
|
||||
import Transform from './transform';
|
||||
|
||||
export default class Geo {
|
||||
constructor(tileSize) {
|
||||
@ -62,8 +63,8 @@ export default class Geo {
|
||||
}
|
||||
|
||||
const pt = this.fromLatLngToCenterPixel(ptLatLng);
|
||||
pt.x -= this.transform_.worldSize *
|
||||
Math.round(pt.x / this.transform_.worldSize);
|
||||
pt.x -=
|
||||
this.transform_.worldSize * Math.round(pt.x / this.transform_.worldSize);
|
||||
|
||||
pt.x += this.transform_.width / 2;
|
||||
pt.y += this.transform_.height / 2;
|
||||
@ -112,7 +113,8 @@ export default class Geo {
|
||||
const bndL = (margins && margins[3]) || 0;
|
||||
|
||||
if (
|
||||
this.getWidth() - bndR - bndL > 0 && this.getHeight() - bndT - bndB > 0
|
||||
this.getWidth() - bndR - bndL > 0 &&
|
||||
this.getHeight() - bndT - bndB > 0
|
||||
) {
|
||||
const topLeftCorner = this.transform_.pointLocation(
|
||||
Point.convert({
|
||||
@ -139,7 +141,7 @@ export default class Geo {
|
||||
];
|
||||
|
||||
if (roundFactor) {
|
||||
res = res.map(r => Math.round(r * roundFactor) / roundFactor);
|
||||
res = res.map((r) => Math.round(r * roundFactor) / roundFactor);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { wrap } from './wrap';
|
||||
|
||||
export default class LatLng {
|
||||
static convert = a => {
|
||||
static convert = (a) => {
|
||||
if (a instanceof LatLng) {
|
||||
return a;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import LatLng from './lat_lng';
|
||||
|
||||
export default class LatLngBounds {
|
||||
static convert = a => {
|
||||
static convert = (a) => {
|
||||
if (!a || a instanceof LatLngBounds) return a;
|
||||
return new LatLngBounds(a);
|
||||
};
|
||||
@ -51,11 +51,11 @@ export default class Transform {
|
||||
}
|
||||
|
||||
get bearing() {
|
||||
return -this.angle / Math.PI * 180;
|
||||
return (-this.angle / Math.PI) * 180;
|
||||
}
|
||||
|
||||
set bearing(bearing) {
|
||||
this.angle = -wrap(bearing, -180, 180) * Math.PI / 180;
|
||||
this.angle = (-wrap(bearing, -180, 180) * Math.PI) / 180;
|
||||
}
|
||||
|
||||
get zoom() {
|
||||
@ -106,24 +106,23 @@ export default class Transform {
|
||||
|
||||
// lat/lon <-> absolute pixel coords convertion
|
||||
lngX(lon, worldSize) {
|
||||
return (180 + lon) * (worldSize || this.worldSize) / 360;
|
||||
return ((180 + lon) * (worldSize || this.worldSize)) / 360;
|
||||
}
|
||||
|
||||
// latitude to absolute y coord
|
||||
latY(lat, worldSize) {
|
||||
const y = 180 /
|
||||
Math.PI *
|
||||
Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360));
|
||||
return (180 - y) * (worldSize || this.worldSize) / 360;
|
||||
const y =
|
||||
(180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360));
|
||||
return ((180 - y) * (worldSize || this.worldSize)) / 360;
|
||||
}
|
||||
|
||||
xLng(x, worldSize) {
|
||||
return x * 360 / (worldSize || this.worldSize) - 180;
|
||||
return (x * 360) / (worldSize || this.worldSize) - 180;
|
||||
}
|
||||
|
||||
yLat(y, worldSize) {
|
||||
const y2 = 180 - y * 360 / (worldSize || this.worldSize);
|
||||
return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
|
||||
const y2 = 180 - (y * 360) / (worldSize || this.worldSize);
|
||||
return (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;
|
||||
}
|
||||
|
||||
locationPoint(latlng) {
|
||||
@ -2,5 +2,5 @@
|
||||
|
||||
export function wrap(n, min, max) {
|
||||
const d = max - min;
|
||||
return n === max ? n : ((n - min) % d + d) % d + min;
|
||||
return n === max ? n : ((((n - min) % d) + d) % d) + min;
|
||||
}
|
||||
@ -1,15 +1,17 @@
|
||||
import log2 from './math/log2';
|
||||
import log2 from '../utils/log2';
|
||||
|
||||
const GOOGLE_TILE_SIZE = 256;
|
||||
|
||||
function latLng2World({ lat, lng }) {
|
||||
const sin = Math.sin(lat * Math.PI / 180);
|
||||
const sin = Math.sin((lat * Math.PI) / 180);
|
||||
const x = lng / 360 + 0.5;
|
||||
let y = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
|
||||
let y = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI;
|
||||
|
||||
y = y < 0 // eslint-disable-line
|
||||
? 0
|
||||
: y > 1 ? 1 : y;
|
||||
? 0
|
||||
: y > 1
|
||||
? 1
|
||||
: y;
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
@ -19,19 +21,21 @@ function world2LatLng({ x, y }) {
|
||||
// TODO test that this is faster
|
||||
// 360 * Math.atan(Math.exp((180 - y * 360) * Math.PI / 180)) / Math.PI - 90;
|
||||
return {
|
||||
lat: 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
|
||||
lat: (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
|
||||
lng: x * 360 - 180,
|
||||
};
|
||||
}
|
||||
|
||||
// Thank you wiki https://en.wikipedia.org/wiki/Geographic_coordinate_system
|
||||
function latLng2MetersPerDegree({ lat }) {
|
||||
const phi = lat * Math.PI / 180;
|
||||
const metersPerLatDegree = 111132.92 -
|
||||
const phi = (lat * Math.PI) / 180;
|
||||
const metersPerLatDegree =
|
||||
111132.92 -
|
||||
559.82 * Math.cos(2 * phi) +
|
||||
1.175 * Math.cos(4 * phi) -
|
||||
0.0023 * Math.cos(6 * phi);
|
||||
const metersPerLngDegree = 111412.84 * Math.cos(phi) -
|
||||
const metersPerLngDegree =
|
||||
111412.84 * Math.cos(phi) -
|
||||
93.5 * Math.cos(3 * phi) +
|
||||
0.118 * Math.cos(5 * phi);
|
||||
return { metersPerLatDegree, metersPerLngDegree };
|
||||
@ -42,8 +46,8 @@ function meters2LatLngBounds(meters, { lat, lng }) {
|
||||
lat,
|
||||
});
|
||||
|
||||
const latDelta = 0.5 * meters / metersPerLatDegree;
|
||||
const lngDelta = 0.5 * meters / metersPerLngDegree;
|
||||
const latDelta = (0.5 * meters) / metersPerLatDegree;
|
||||
const lngDelta = (0.5 * meters) / metersPerLngDegree;
|
||||
|
||||
return {
|
||||
nw: {
|
||||
@ -71,9 +75,8 @@ function fitNwSe(nw, se, width, height) {
|
||||
const EPS = 0.000000001;
|
||||
const nwWorld = latLng2World(nw);
|
||||
const seWorld = latLng2World(se);
|
||||
const dx = nwWorld.x < seWorld.x
|
||||
? seWorld.x - nwWorld.x
|
||||
: 1 - nwWorld.x + seWorld.x;
|
||||
const dx =
|
||||
nwWorld.x < seWorld.x ? seWorld.x - nwWorld.x : 1 - nwWorld.x + seWorld.x;
|
||||
const dy = seWorld.y - nwWorld.y;
|
||||
|
||||
if (dx <= 0 && dy <= 0) {
|
||||
@ -87,10 +90,10 @@ function fitNwSe(nw, se, width, height) {
|
||||
// TODO find center just unproject middle world point
|
||||
const middle = {
|
||||
x: nwWorld.x < seWorld.x // eslint-disable-line
|
||||
? 0.5 * (nwWorld.x + seWorld.x)
|
||||
: nwWorld.x + seWorld.x - 1 > 0
|
||||
? 0.5 * (nwWorld.x + seWorld.x - 1)
|
||||
: 0.5 * (1 + nwWorld.x + seWorld.x),
|
||||
? 0.5 * (nwWorld.x + seWorld.x)
|
||||
: nwWorld.x + seWorld.x - 1 > 0
|
||||
? 0.5 * (nwWorld.x + seWorld.x - 1)
|
||||
: 0.5 * (1 + nwWorld.x + seWorld.x),
|
||||
y: 0.5 * (nwWorld.y + seWorld.y),
|
||||
};
|
||||
|
||||
@ -181,11 +184,11 @@ export function meters2ScreenPixels(meters, { lat, lng }, zoom) {
|
||||
// Helper functions for working with svg tiles, (examples coming soon)
|
||||
|
||||
export function tile2LatLng({ x, y }, zoom) {
|
||||
const n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom);
|
||||
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom);
|
||||
|
||||
return {
|
||||
lat: 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
|
||||
lng: x / Math.pow(2, zoom) * 360 - 180,
|
||||
lat: (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
|
||||
lng: (x / Math.pow(2, zoom)) * 360 - 180,
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ let loadPromise_;
|
||||
|
||||
let resolveCustomPromise_;
|
||||
|
||||
const _customPromise = new Promise(resolve => {
|
||||
const _customPromise = new Promise((resolve) => {
|
||||
resolveCustomPromise_ = resolve;
|
||||
});
|
||||
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
/* eslint-disable */
|
||||
import './utils/jsdomInit.js';
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TestUtils from 'react-dom/test-utils';
|
||||
|
||||
import expect from 'expect';
|
||||
|
||||
import TestUtils from 'react-dom/test-utils';
|
||||
import GoogleMap from '../../src/google_map';
|
||||
import GoogleMap from '../../google_map';
|
||||
|
||||
describe('Components', () => {
|
||||
it('Should work', () => {
|
||||
@ -149,7 +147,7 @@ describe('Components', () => {
|
||||
|
||||
it('Should call custom loader', () => {
|
||||
const API_KEY = 'API_KEY';
|
||||
const spy = expect.createSpy(() => {});
|
||||
const spy = expect.createSpy(() => { });
|
||||
const asyncSpy = async a => spy(a);
|
||||
|
||||
TestUtils.renderIntoDocument(
|
||||
@ -30,29 +30,29 @@ var attachEvent = typeof document !== 'undefined' && document.attachEvent;
|
||||
var stylesCreated = false;
|
||||
|
||||
if (canUseDOM && !attachEvent) {
|
||||
var requestFrame = (function() {
|
||||
var requestFrame = (function () {
|
||||
var raf = _window.requestAnimationFrame ||
|
||||
_window.mozRequestAnimationFrame ||
|
||||
_window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
function (fn) {
|
||||
return _window.setTimeout(fn, 20);
|
||||
};
|
||||
return function(fn) {
|
||||
return function (fn) {
|
||||
return raf(fn);
|
||||
};
|
||||
})();
|
||||
|
||||
var cancelFrame = (function() {
|
||||
var cancelFrame = (function () {
|
||||
var cancel = _window.cancelAnimationFrame ||
|
||||
_window.mozCancelAnimationFrame ||
|
||||
_window.webkitCancelAnimationFrame ||
|
||||
_window.clearTimeout;
|
||||
return function(id) {
|
||||
return function (id) {
|
||||
return cancel(id);
|
||||
};
|
||||
})();
|
||||
|
||||
var resetTriggers = function(element) {
|
||||
var resetTriggers = function (element) {
|
||||
var triggers = element.__resizeTriggers__,
|
||||
expand = triggers.firstElementChild,
|
||||
contract = triggers.lastElementChild,
|
||||
@ -65,20 +65,20 @@ if (canUseDOM && !attachEvent) {
|
||||
expand.scrollTop = expand.scrollHeight;
|
||||
};
|
||||
|
||||
var checkTriggers = function(element) {
|
||||
var checkTriggers = function (element) {
|
||||
return element.offsetWidth != element.__resizeLast__.width ||
|
||||
element.offsetHeight != element.__resizeLast__.height;
|
||||
};
|
||||
|
||||
var scrollListener = function(e) {
|
||||
var scrollListener = function (e) {
|
||||
var element = this;
|
||||
resetTriggers(this);
|
||||
if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
|
||||
this.__resizeRAF__ = requestFrame(function() {
|
||||
this.__resizeRAF__ = requestFrame(function () {
|
||||
if (checkTriggers(element)) {
|
||||
element.__resizeLast__.width = element.offsetWidth;
|
||||
element.__resizeLast__.height = element.offsetHeight;
|
||||
element.__resizeListeners__.forEach(function(fn) {
|
||||
element.__resizeListeners__.forEach(function (fn) {
|
||||
fn.call(element, e);
|
||||
});
|
||||
}
|
||||
@ -128,7 +128,7 @@ if (canUseDOM && !attachEvent) {
|
||||
'; ';
|
||||
}
|
||||
|
||||
var createStyles = function() {
|
||||
var createStyles = function () {
|
||||
if (!stylesCreated) {
|
||||
//opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360
|
||||
var css = (animationKeyframes ? animationKeyframes : '') +
|
||||
@ -151,7 +151,7 @@ var createStyles = function() {
|
||||
}
|
||||
};
|
||||
|
||||
var addResizeListener = function(element, fn) {
|
||||
var addResizeListener = function (element, fn) {
|
||||
if (element.parentNode === undefined) {
|
||||
var tempParentDiv = document.createElement('div');
|
||||
element.parentNode = tempParentDiv;
|
||||
@ -180,7 +180,7 @@ var addResizeListener = function(element, fn) {
|
||||
animationstartevent &&
|
||||
element.__resizeTriggers__.addEventListener(
|
||||
animationstartevent,
|
||||
function(e) {
|
||||
function (e) {
|
||||
if (e.animationName == animationName) resetTriggers(element);
|
||||
}
|
||||
);
|
||||
@ -189,7 +189,7 @@ var addResizeListener = function(element, fn) {
|
||||
}
|
||||
};
|
||||
|
||||
var removeResizeListener = function(element, fn) {
|
||||
var removeResizeListener = function (element, fn) {
|
||||
element = element.parentNode;
|
||||
if (attachEvent)
|
||||
element.detachEvent('onresize', fn);
|
||||
@ -207,7 +207,7 @@ var removeResizeListener = function(element, fn) {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addResizeListener: addResizeListener,
|
||||
removeResizeListener: removeResizeListener,
|
||||
export {
|
||||
addResizeListener,
|
||||
removeResizeListener,
|
||||
};
|
||||
|
||||
@ -1 +0,0 @@
|
||||
export * from './utils.js';
|
||||
@ -1,4 +1,4 @@
|
||||
const isEmpty = val => {
|
||||
const isEmpty = (val) => {
|
||||
// check for empty object {}, array []
|
||||
if (val !== null && typeof val === 'object') {
|
||||
if (Object.keys(val).length === 0) {
|
||||
|
||||
@ -6,6 +6,8 @@ const objectToString = Object.prototype.toString;
|
||||
|
||||
export default function isNumber(value) {
|
||||
const numberTag = '[object Number]';
|
||||
return typeof value === 'number' ||
|
||||
(isObjectLike(value) && objectToString.call(value) === numberTag);
|
||||
return (
|
||||
typeof value === 'number' ||
|
||||
(isObjectLike(value) && objectToString.call(value) === numberTag)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// source taken from https://github.com/rackt/redux/blob/master/src/utils/isPlainObject.js
|
||||
const fnToString = fn => Function.prototype.toString.call(fn);
|
||||
const fnToString = (fn) => Function.prototype.toString.call(fn);
|
||||
|
||||
/**
|
||||
* @param {any} obj The object to inspect.
|
||||
@ -10,9 +10,10 @@ export default function isPlainObject(obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const proto = typeof obj.constructor === 'function'
|
||||
? Object.getPrototypeOf(obj)
|
||||
: Object.prototype;
|
||||
const proto =
|
||||
typeof obj.constructor === 'function'
|
||||
? Object.getPrototypeOf(obj)
|
||||
: Object.prototype;
|
||||
|
||||
if (proto === null) {
|
||||
return true;
|
||||
@ -20,7 +21,9 @@ export default function isPlainObject(obj) {
|
||||
|
||||
const constructor = proto.constructor;
|
||||
|
||||
return typeof constructor === 'function' &&
|
||||
return (
|
||||
typeof constructor === 'function' &&
|
||||
constructor instanceof constructor &&
|
||||
fnToString(constructor) === fnToString(Object);
|
||||
fnToString(constructor) === fnToString(Object)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
###Код выдран из mapboxgl чтобы преобразования координат не зависели от библиотеки
|
||||
|
||||
3
src/utils/log2.js
Normal file
3
src/utils/log2.js
Normal file
@ -0,0 +1,3 @@
|
||||
const log2 = Math.log2 ? Math.log2 : (x) => Math.log(x) / Math.LN2;
|
||||
|
||||
export default log2;
|
||||
@ -1,3 +0,0 @@
|
||||
const log2 = Math.log2 ? Math.log2 : x => Math.log(x) / Math.LN2;
|
||||
|
||||
export default log2;
|
||||
@ -1,13 +1,10 @@
|
||||
// source taken from https://github.com/rackt/redux/blob/master/src/utils/pick.js
|
||||
|
||||
export default function pick(obj, fn) {
|
||||
return Object.keys(obj).reduce(
|
||||
(result, key) => {
|
||||
if (fn(obj[key])) {
|
||||
return Object.keys(obj).reduce((result, key) => {
|
||||
if (fn(obj[key])) {
|
||||
result[key] = obj[key]; // eslint-disable-line
|
||||
}
|
||||
return result;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ export default function raf(callback) {
|
||||
return window.requestAnimationFrame(callback);
|
||||
}
|
||||
|
||||
const nativeRaf = window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame;
|
||||
const nativeRaf =
|
||||
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
|
||||
|
||||
return nativeRaf
|
||||
? nativeRaf(callback)
|
||||
|
||||
@ -15,7 +15,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
* inlined Object.is polyfill to avoid requiring consumers ship their own
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
||||
*/
|
||||
function is(x: mixed, y: mixed): boolean {
|
||||
function is(x, y) {
|
||||
// SameValue algorithm
|
||||
if (x === y) {
|
||||
// Steps 1-5, 7-10
|
||||
@ -33,7 +33,7 @@ function is(x: mixed, y: mixed): boolean {
|
||||
* when any key has values which are not strictly equal between the arguments.
|
||||
* Returns true when the values of all keys are strictly equal.
|
||||
*/
|
||||
function shallowEqual(objA: mixed, objB: mixed): boolean {
|
||||
function shallowEqual(objA, objB) {
|
||||
if (is(objA, objB)) {
|
||||
return true;
|
||||
}
|
||||
@ -67,5 +67,5 @@ function shallowEqual(objA: mixed, objB: mixed): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = shallowEqual;
|
||||
export default shallowEqual;
|
||||
/* src: https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js */
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
/* eslint-disable */
|
||||
import jsdom from 'jsdom';
|
||||
|
||||
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');
|
||||
const win = doc.defaultView;
|
||||
global.document = doc;
|
||||
global.window = win;
|
||||
|
||||
function propagateToGlobal(window) {
|
||||
for (const key in window) {
|
||||
if (!window.hasOwnProperty(key)) continue;
|
||||
if (key in global) continue;
|
||||
|
||||
global[key] = window[key];
|
||||
}
|
||||
}
|
||||
|
||||
propagateToGlobal(win);
|
||||
@ -1,2 +0,0 @@
|
||||
var utils = require('../lib/utils'); // eslint-disable-line
|
||||
module.exports = utils;
|
||||
@ -1,27 +0,0 @@
|
||||
const reactExternal = {
|
||||
root: 'React',
|
||||
commonjs2: 'react',
|
||||
commonjs: 'react',
|
||||
amd: 'react',
|
||||
};
|
||||
|
||||
const reactDomExternal = {
|
||||
root: 'ReactDOM',
|
||||
commonjs2: 'react-dom',
|
||||
commonjs: 'react-dom',
|
||||
amd: 'react-dom',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
library: 'GoogleMapReact',
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
externals: {
|
||||
react: reactExternal,
|
||||
'react-dom': reactDomExternal,
|
||||
},
|
||||
module: {
|
||||
loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' }],
|
||||
},
|
||||
};
|
||||
@ -1,12 +0,0 @@
|
||||
const webpack = require('webpack');
|
||||
const baseConfig = require('./webpack.config.base');
|
||||
|
||||
const config = Object.create(baseConfig);
|
||||
config.plugins = [
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('development'),
|
||||
}),
|
||||
];
|
||||
|
||||
module.exports = config;
|
||||
@ -1,18 +0,0 @@
|
||||
const webpack = require('webpack');
|
||||
const baseConfig = require('./webpack.config.base');
|
||||
|
||||
const config = Object.create(baseConfig);
|
||||
config.plugins = [
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compressor: {
|
||||
screw_ie8: true,
|
||||
warnings: false,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
module.exports = config;
|
||||
Loading…
x
Reference in New Issue
Block a user