mirror of
https://github.com/google-map-react/google-map-react.git
synced 2025-12-08 18:26:32 +00:00
Reset Bounds when surrounding div resizes (#219)
* added listeners for element/map resize event * changed names accordingly * added eslint-disabled to pass through travis checks * changes for travis checks * added eslint disables * disabling eslint for lNo 906 * removed trailing spaces * removed unnecessary id and refs * removed eventListners on unMount
This commit is contained in:
parent
7542b69310
commit
e196e9f1e5
110
develop/GMapResizable.js
Normal file
110
develop/GMapResizable.js
Normal file
@ -0,0 +1,110 @@
|
||||
/* eslint-disable */
|
||||
import React, { PropTypes } from 'react';
|
||||
import compose from 'recompose/compose';
|
||||
import defaultProps from 'recompose/defaultProps';
|
||||
import withStateSelector from './utils/withStateSelector';
|
||||
import withHandlers from 'recompose/withHandlers';
|
||||
import withState from 'recompose/withState';
|
||||
import withContext from 'recompose/withContext';
|
||||
import withProps from 'recompose/withProps';
|
||||
import withPropsOnChange from 'recompose/withPropsOnChange';
|
||||
import ptInBounds from './utils/ptInBounds';
|
||||
import GoogleMapReact from '../src';
|
||||
import SimpleMarker from './markers/SimpleMarker';
|
||||
import { createSelector } from 'reselect';
|
||||
import { susolvkaCoords, generateMarkers } from './data/fakeData';
|
||||
|
||||
export const gMapResizable = ({
|
||||
style, hoverDistance, options,
|
||||
mapParams: { center, zoom },
|
||||
onChange, onChildMouseEnter, onChildMouseLeave,
|
||||
markers, draggable, // hoveredMarkerId,
|
||||
}) => (
|
||||
<GoogleMapReact
|
||||
draggable={draggable}
|
||||
style={style}
|
||||
options={options}
|
||||
hoverDistance={hoverDistance}
|
||||
center={center}
|
||||
zoom={zoom}
|
||||
onChange={onChange}
|
||||
onChildMouseEnter={onChildMouseEnter}
|
||||
onChildMouseLeave={onChildMouseLeave}
|
||||
resetBoundsOnResize={true}
|
||||
apiKey={"AIzaSyC-BebC7ChnHPzxQm7DAHYFMCqR5H3Jlps"}
|
||||
>
|
||||
{
|
||||
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);
|
||||
@ -14,6 +14,7 @@ export class Layout extends Component { // eslint-disable-line
|
||||
<Link to="/">Multi Markers</Link>
|
||||
<Link to="/hoverunoptim">Hover unoptim</Link>
|
||||
<Link to="/hoveroptim">Hover optim</Link>
|
||||
<Link to="/resizable">Resizable Map</Link>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/istarkov/google-map-clustering-example">
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
.layout
|
||||
display: flex
|
||||
min-height: 100vh
|
||||
min-height: 90vh
|
||||
flex-direction: column
|
||||
margin: 0 1px 0 1px
|
||||
|
||||
width: 100vh
|
||||
.header
|
||||
height: 2em
|
||||
background-color: #004336
|
||||
@ -29,6 +29,8 @@
|
||||
|
||||
.main
|
||||
flex: 1
|
||||
resize: both
|
||||
overflow: auto
|
||||
display: flex
|
||||
|
||||
.footer
|
||||
@ -39,4 +41,4 @@
|
||||
align-items: center
|
||||
justify-content: center
|
||||
a
|
||||
color: #fff
|
||||
color: #fff
|
||||
@ -5,6 +5,7 @@ import { Router, Route, IndexRoute, browserHistory } from 'react-router';
|
||||
import Layout from './Layout';
|
||||
import GMap from './GMap';
|
||||
import GMapOptim from './GMapOptim';
|
||||
import GMapResizable from './GMapResizable';
|
||||
|
||||
import 'normalize.css/normalize.css';
|
||||
import './Main.sass';
|
||||
@ -16,6 +17,7 @@ render(
|
||||
<Route path="/" component={Layout}>
|
||||
<Route markersCount={50} path="hoverunoptim" component={GMap} />
|
||||
<Route markersCount={50} path="hoveroptim" component={GMapOptim} />
|
||||
<Route markersCount={20} path="resizable" component={GMapResizable} />
|
||||
<IndexRoute markersCount={20} component={GMap} />
|
||||
</Route>
|
||||
</Router>
|
||||
|
||||
@ -22,6 +22,7 @@ import log2 from './utils/math/log2';
|
||||
|
||||
import isNumber from './utils/isNumber';
|
||||
import omit from './utils/omit';
|
||||
import detectElementResize from './utils/detectElementResize';
|
||||
|
||||
const kEPS = 0.00001;
|
||||
const K_GOOGLE_TILE_SIZE = 256;
|
||||
@ -89,6 +90,7 @@ export default class GoogleMap extends Component {
|
||||
yesIWantToUseGoogleMapApiInternals: PropTypes.bool,
|
||||
draggable: PropTypes.bool,
|
||||
style: PropTypes.any,
|
||||
resetBoundsOnResize: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -184,7 +186,7 @@ export default class GoogleMap extends Component {
|
||||
this.mounted_ = true;
|
||||
window.addEventListener('resize', this._onWindowResize);
|
||||
window.addEventListener('keydown', this._onKeyDownCapture, true);
|
||||
|
||||
const mapDom = ReactDOM.findDOMNode(this.refs.google_map_dom);
|
||||
// gmap can't prevent map drag if mousedown event already occured
|
||||
// the only workaround I find is prevent mousedown native browser event
|
||||
ReactDOM.findDOMNode(this.refs.google_map_dom)
|
||||
@ -205,6 +207,10 @@ export default class GoogleMap extends Component {
|
||||
this._initMap();
|
||||
}
|
||||
}, 0, this);
|
||||
if (this.props.resetBoundsOnResize) {
|
||||
const that = this;
|
||||
detectElementResize.addResizeListener(mapDom, that._mapDomResizeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -301,12 +307,13 @@ export default class GoogleMap extends Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted_ = false;
|
||||
|
||||
const that = this;
|
||||
const mapDom = ReactDOM.findDOMNode(this.refs.google_map_dom);
|
||||
window.removeEventListener('resize', this._onWindowResize);
|
||||
window.removeEventListener('keydown', this._onKeyDownCapture);
|
||||
ReactDOM.findDOMNode(this.refs.google_map_dom)
|
||||
.removeEventListener('mousedown', this._onMapMouseDownNative, true);
|
||||
mapDom.removeEventListener('mousedown', this._onMapMouseDownNative, true);
|
||||
window.removeEventListener('mouseup', this._onChildMouseUp, false);
|
||||
detectElementResize.addResizeListener(mapDom, that._mapDomResizeCallback);
|
||||
|
||||
if (this.overlay_) {
|
||||
// this triggers overlay_.onRemove(), which will unmount the <GoogleMapMarkers/>
|
||||
@ -326,7 +333,6 @@ export default class GoogleMap extends Component {
|
||||
delete this.map_;
|
||||
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
|
||||
@ -364,6 +370,13 @@ export default class GoogleMap extends Component {
|
||||
return minZoom;
|
||||
}
|
||||
|
||||
_mapDomResizeCallback = () => {
|
||||
this.resetSizeOnIdle_ = true;
|
||||
if (this.maps_) {
|
||||
this.maps_.event.trigger(this.map_, 'resize');
|
||||
}
|
||||
}
|
||||
|
||||
_initMap = () => {
|
||||
// only initialize the map once
|
||||
if (this.initialized_) {
|
||||
@ -886,7 +899,9 @@ export default class GoogleMap extends Component {
|
||||
[centerLatLng.lat, centerLatLng.lng], [gmC.lat(), gmC.lng()]);
|
||||
}
|
||||
|
||||
if (!isArraysEqualEps(bounds, [ne.lat(), sw.lng(), sw.lat(), ne.lng()], kEPS)) {
|
||||
if (!isArraysEqualEps(bounds, // eslint-disable-line
|
||||
[ne.lat(), sw.lng(), sw.lat(), ne.lng()], kEPS // eslint-disable-line no-console
|
||||
) && !this.props.resetBoundsOnResize) { // eslint-disable-line no-console
|
||||
// this is normal if this message occured on resize
|
||||
console.info('GoogleMap bounds not eq:', '\n', // eslint-disable-line no-console
|
||||
bounds, '\n', [ne.lat(), sw.lng(), sw.lat(), ne.lng()]);
|
||||
|
||||
168
src/utils/detectElementResize.js
Normal file
168
src/utils/detectElementResize.js
Normal file
@ -0,0 +1,168 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Detect Element Resize.
|
||||
* Forked in order to guard against unsafe 'window' and 'document' references.
|
||||
*
|
||||
* https://github.com/sdecima/javascript-detect-element-resize
|
||||
* Sebastian Decima
|
||||
*
|
||||
* version: 0.5.3
|
||||
**/
|
||||
|
||||
// Check `document` and `window` in case of server-side rendering
|
||||
var _window
|
||||
if (typeof window !== 'undefined') {
|
||||
_window = window
|
||||
} else if (typeof self !== 'undefined') {
|
||||
_window = self
|
||||
} else {
|
||||
_window = this
|
||||
}
|
||||
|
||||
var attachEvent = typeof document !== 'undefined' && document.attachEvent;
|
||||
var stylesCreated = false;
|
||||
|
||||
if (!attachEvent) {
|
||||
var requestFrame = (function(){
|
||||
var raf = _window.requestAnimationFrame || _window.mozRequestAnimationFrame || _window.webkitRequestAnimationFrame ||
|
||||
function(fn){ return _window.setTimeout(fn, 20); };
|
||||
return function(fn){ return raf(fn); };
|
||||
})();
|
||||
|
||||
var cancelFrame = (function(){
|
||||
var cancel = _window.cancelAnimationFrame || _window.mozCancelAnimationFrame || _window.webkitCancelAnimationFrame ||
|
||||
_window.clearTimeout;
|
||||
return function(id){ return cancel(id); };
|
||||
})();
|
||||
|
||||
var resetTriggers = function(element){
|
||||
var triggers = element.__resizeTriggers__,
|
||||
expand = triggers.firstElementChild,
|
||||
contract = triggers.lastElementChild,
|
||||
expandChild = expand.firstElementChild;
|
||||
contract.scrollLeft = contract.scrollWidth;
|
||||
contract.scrollTop = contract.scrollHeight;
|
||||
expandChild.style.width = expand.offsetWidth + 1 + 'px';
|
||||
expandChild.style.height = expand.offsetHeight + 1 + 'px';
|
||||
expand.scrollLeft = expand.scrollWidth;
|
||||
expand.scrollTop = expand.scrollHeight;
|
||||
};
|
||||
|
||||
var checkTriggers = function(element){
|
||||
return element.offsetWidth != element.__resizeLast__.width ||
|
||||
element.offsetHeight != element.__resizeLast__.height;
|
||||
}
|
||||
|
||||
var scrollListener = function(e){
|
||||
var element = this;
|
||||
resetTriggers(this);
|
||||
if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
|
||||
this.__resizeRAF__ = requestFrame(function(){
|
||||
if (checkTriggers(element)) {
|
||||
element.__resizeLast__.width = element.offsetWidth;
|
||||
element.__resizeLast__.height = element.offsetHeight;
|
||||
element.__resizeListeners__.forEach(function(fn){
|
||||
fn.call(element, e);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* Detect CSS Animations support to detect element display/re-attach */
|
||||
var animation = false,
|
||||
animationstring = 'animation',
|
||||
keyframeprefix = '',
|
||||
animationstartevent = 'animationstart',
|
||||
domPrefixes = 'Webkit Moz O ms'.split(' '),
|
||||
startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '),
|
||||
pfx = '';
|
||||
{
|
||||
var elm = document.createElement('fakeelement');
|
||||
if( elm.style.animationName !== undefined ) { animation = true; }
|
||||
|
||||
if( animation === false ) {
|
||||
for( var i = 0; i < domPrefixes.length; i++ ) {
|
||||
if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) {
|
||||
pfx = domPrefixes[ i ];
|
||||
animationstring = pfx + 'Animation';
|
||||
keyframeprefix = '-' + pfx.toLowerCase() + '-';
|
||||
animationstartevent = startEvents[ i ];
|
||||
animation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var animationName = 'resizeanim';
|
||||
var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } ';
|
||||
var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; ';
|
||||
}
|
||||
|
||||
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 : '') +
|
||||
'.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' +
|
||||
'.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }',
|
||||
head = document.head || document.getElementsByTagName('head')[0],
|
||||
style = document.createElement('style');
|
||||
|
||||
style.type = 'text/css';
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(css));
|
||||
}
|
||||
|
||||
head.appendChild(style);
|
||||
stylesCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
var addResizeListener = function(element, fn){
|
||||
if (element.parentNode === undefined) {
|
||||
var tempParentDiv = document.createElement('div');
|
||||
element.parentNode = tempParentDiv;
|
||||
}
|
||||
element = element.parentNode;
|
||||
if (attachEvent) element.attachEvent('onresize', fn);
|
||||
else {
|
||||
if (!element.__resizeTriggers__) {
|
||||
if (getComputedStyle(element).position == 'static') element.style.position = 'relative';
|
||||
createStyles();
|
||||
element.__resizeLast__ = {};
|
||||
element.__resizeListeners__ = [];
|
||||
(element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers';
|
||||
element.__resizeTriggers__.innerHTML = '<div class="expand-trigger"><div></div></div>' +
|
||||
'<div class="contract-trigger"></div>';
|
||||
element.appendChild(element.__resizeTriggers__);
|
||||
resetTriggers(element);
|
||||
element.addEventListener('scroll', scrollListener, true);
|
||||
|
||||
/* Listen for a css animation to detect element display/re-attach */
|
||||
animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) {
|
||||
if(e.animationName == animationName)
|
||||
resetTriggers(element);
|
||||
});
|
||||
}
|
||||
element.__resizeListeners__.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
var removeResizeListener = function(element, fn){
|
||||
element = element.parentNode;
|
||||
if (attachEvent) element.detachEvent('onresize', fn);
|
||||
else {
|
||||
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
|
||||
if (!element.__resizeListeners__.length) {
|
||||
element.removeEventListener('scroll', scrollListener);
|
||||
element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addResizeListener : addResizeListener,
|
||||
removeResizeListener : removeResizeListener
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user