mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Life cycle handling of React based PriceLabel was buggy - revert to plain examples of google overlays
This commit is contained in:
parent
4e291af482
commit
ce47dc329e
3 changed files with 198 additions and 38 deletions
|
|
@ -1,4 +1,91 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.labelRoot {
|
||||
position: relative;
|
||||
|
||||
transform: translate( -50%, -31px);
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.priceLabel {
|
||||
/**
|
||||
* Since caret is absolutely positioned,
|
||||
* label must have relative to be included to the same rendering layer
|
||||
*/
|
||||
position: relative;
|
||||
|
||||
/* Font */
|
||||
@apply --marketplaceH5FontStyles;
|
||||
font-weight: var(--fontWeightSemiBold);
|
||||
color: var(--matterColor);
|
||||
letter-spacing: 0.1px;
|
||||
|
||||
background-color: var(--matterColorLight);
|
||||
|
||||
/* Borders */
|
||||
border-style: solid;
|
||||
border-color: var(--matterColorNegative);
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--boxShadowPopupLight);
|
||||
|
||||
/* Dimensions */
|
||||
padding: 6px 10px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
transition: var(--transitionStyleButton);
|
||||
|
||||
&:hover {
|
||||
color: var(--marketplaceColor);
|
||||
cursor: pointer;
|
||||
box-shadow: var(--boxShadowPopup);
|
||||
}
|
||||
|
||||
/* Overwrite dimensions from font styles */
|
||||
@media (--viewportMedium) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.caretShadow {
|
||||
/* Caret / arrow dimensions and position */
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
transform: rotate(45deg);
|
||||
|
||||
/* Caret should have same box-shadow as label */
|
||||
box-shadow: var(--boxShadowPopupLight);
|
||||
}
|
||||
|
||||
.caret {
|
||||
|
||||
/* Caret / arrow dimensions and position */
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
transform: rotate(45deg);
|
||||
|
||||
/* Caret should have same bg-color and border as label */
|
||||
background-color: var(--matterColorLight);
|
||||
border-right-style: solid;
|
||||
border-right-color: var(--matterColorNegative);
|
||||
border-right-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: var(--matterColorNegative);
|
||||
border-bottom-width: 1px;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { isEqualWith, sortBy } from 'lodash';
|
||||
import { unmountComponentAtNode } from 'react-dom';
|
||||
import { isEqualWith } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { types as sdkTypes } from '../../util/sdkLoader';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { MapPriceMarker } from '../../components';
|
||||
|
||||
import css from './SearchMap.css';
|
||||
|
||||
|
|
@ -17,9 +17,13 @@ const fitMapToBounds = (map, bounds) => {
|
|||
}
|
||||
};
|
||||
|
||||
// Compare listing arrays (if listings are the same, there's no need to rerender google maps).
|
||||
const hasSameListings = (prevListings, nextListings) => {
|
||||
return isEqualWith(sortBy(prevListings, l => l.id.uuid), sortBy(nextListings, l => l.id.uuid));
|
||||
const createDiv = (className, text) => {
|
||||
const d = document.createElement(`div`);
|
||||
d.className = className;
|
||||
if (text) {
|
||||
d.appendChild(document.createTextNode(text));
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
class SearchMap extends Component {
|
||||
|
|
@ -27,8 +31,7 @@ class SearchMap extends Component {
|
|||
super(props);
|
||||
|
||||
this.map = null;
|
||||
this.state = { overlays: [] };
|
||||
this.addOverlay = this.addOverlay.bind(this);
|
||||
this.priceLabelOverlays = [];
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -49,10 +52,92 @@ class SearchMap extends Component {
|
|||
// Disable zooming by scrolling
|
||||
scrollwheel: false,
|
||||
};
|
||||
|
||||
// Create map
|
||||
this.map = new window.google.maps.Map(this.el, mapOptions);
|
||||
|
||||
// If bounds are given, use it (defaults to center & zoom).
|
||||
fitMapToBounds(this.map, bounds);
|
||||
|
||||
// This is now done according to Google Maps examples
|
||||
// https://developers.google.com/maps/documentation/javascript/examples/overlay-simple
|
||||
// TODO It should be possible to do this more React way, but that enhancement must be done later
|
||||
this.MapPriceOverlayMarker = class extends window.google.maps.OverlayView {
|
||||
constructor(id, origin, map) {
|
||||
super();
|
||||
|
||||
// Initialize all properties.
|
||||
this.id = id;
|
||||
this.origin = origin;
|
||||
this.map = map;
|
||||
|
||||
// Define a property to hold the price label.
|
||||
// This happens in onAdd method
|
||||
this.overlayContainer = null;
|
||||
|
||||
// Explicitly call setMap on this overlay.
|
||||
// This will trigger calls to onAdd and draw methods
|
||||
this.setMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* onAdd is called when the map's panes are ready and the overlay has been
|
||||
* added to the map.
|
||||
*/
|
||||
onAdd() {
|
||||
const overlayDiv = document.createElement(`div`);
|
||||
overlayDiv.style.position = `absolute`;
|
||||
overlayDiv.dataset.overlayId = this.id;
|
||||
this.overlayContainer = overlayDiv;
|
||||
|
||||
// Create price label
|
||||
// TODO this should be possible with React too,
|
||||
// but life-cycle management needs some some extra research
|
||||
const root = createDiv(css.labelRoot);
|
||||
root.appendChild(createDiv(css.caretShadow));
|
||||
root.appendChild(createDiv(css.priceLabel, 'asdf'));
|
||||
root.appendChild(createDiv(css.caret));
|
||||
this.overlayContainer.appendChild(root);
|
||||
|
||||
// Add the element to the "overlayMouseTarget" pane.
|
||||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapPanes
|
||||
const panes = this.getPanes();
|
||||
panes.overlayMouseTarget.appendChild(overlayDiv);
|
||||
}
|
||||
|
||||
draw() {
|
||||
// We need to retrieve the projection from the overlay, to map pixels to coordinates
|
||||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapCanvasProjection
|
||||
const overlayProjection = this.getProjection();
|
||||
const point = overlayProjection.fromLatLngToDivPixel(this.origin);
|
||||
|
||||
if (point) {
|
||||
this.overlayContainer.style.left = `${point.x}px`;
|
||||
this.overlayContainer.style.top = `${point.y}px`;
|
||||
this.overlayContainer.style.height = `0`;
|
||||
}
|
||||
|
||||
window.google.maps.event.addDomListener(this.overlayContainer, 'click', event => {
|
||||
// TODO: Click handler needs to open info window at some point
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`OverlayLayer (${event.target.closest('[data-overlay-id]').dataset.overlayId}) clicked.`
|
||||
);
|
||||
window.google.maps.event.trigger(self, 'click');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The onRemove() method will be called automatically from the API if
|
||||
* we ever set the overlay's map property to 'null'.
|
||||
*/
|
||||
onRemove() {
|
||||
// Remove container from parent node and tell React about it too
|
||||
this.overlayContainer.parentNode.removeChild(this.overlayContainer);
|
||||
unmountComponentAtNode(this.overlayContainer);
|
||||
this.overlayContainer = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
|
@ -60,48 +145,36 @@ class SearchMap extends Component {
|
|||
|
||||
fitMapToBounds(this.map, bounds);
|
||||
|
||||
if (!hasSameListings(listings, nextProps.listings)) {
|
||||
const hasSameListings = isEqualWith(listings, this.props.listings);
|
||||
const hasOverlaysOnMap = this.map && this.priceLabelOverlays.length > 0;
|
||||
if (!(hasSameListings && hasOverlaysOnMap)) {
|
||||
// Clear markers from map
|
||||
this.state.overlays.forEach(o => o.setMap(null));
|
||||
this.priceLabelOverlays.forEach(o => o.setMap(null));
|
||||
this.priceLabelOverlays = [];
|
||||
|
||||
this.priceLabelOverlays = listings.reverse().map(l => {
|
||||
const geolocation = l.attributes.geolocation;
|
||||
const googleLatLng = new window.google.maps.LatLng({
|
||||
lat: geolocation.lat,
|
||||
lng: geolocation.lng,
|
||||
});
|
||||
|
||||
return new this.MapPriceOverlayMarker(l.id.uuid, googleLatLng, this.map);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addOverlay(overlay) {
|
||||
this.setState(prevState => {
|
||||
// Track map overlays, in case we need to clear them from map
|
||||
const overlays = prevState.overlays.concat([overlay]);
|
||||
return { overlays };
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, rootClassName, listings } = this.props;
|
||||
const { className, rootClassName } = this.props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
// Add listing markers to map if listings prop is passed
|
||||
// By reversing the order, we can show the nearest price labels on top of more distant ones.
|
||||
const currentMarkers = listings
|
||||
.map(l => {
|
||||
return (
|
||||
<MapPriceMarker
|
||||
key={l.id.uuid}
|
||||
map={this.map}
|
||||
listing={l}
|
||||
onAddOverlay={this.addOverlay}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.reverse();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes}
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
>
|
||||
{currentMarkers}
|
||||
</div>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ input,
|
|||
textarea,
|
||||
select,
|
||||
li {
|
||||
@apply --marketplaceDefaultFontStyles
|
||||
@apply --marketplaceDefaultFontStyles;
|
||||
}
|
||||
|
||||
p,
|
||||
|
|
@ -38,7 +38,7 @@ pre {
|
|||
/* TODO */
|
||||
|
||||
html {
|
||||
@apply --marketplaceDefaultFontStyles
|
||||
@apply --marketplaceDefaultFontStyles;
|
||||
color: var(--matterColor);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue