New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a simple custom control class #310
Comments
I defined import React from 'react'
import ReactDOM from 'react-dom'
// Enables custom elements within <GoogleMap>
// Children shouldn't change height between renders
export const MapControl = React.createClass({
componentDidMount() { this._render() },
componentDidUpdate() { this._render() },
componentWillUnmount() {
const {mapHolderRef, controlPosition} = this.props
const index = mapHolderRef.getMap().controls[controlPosition].getArray().indexOf(this.el)
mapHolderRef.getMap().controls[controlPosition].removeAt(index)
},
_render() {
const {mapHolderRef, controlPosition, children} = this.props
ReactDOM.render(
<div
ref={el => {
const controlSet = mapHolderRef.getMap().controls[controlPosition]
if (!this.renderedOnce) {
this.el = el
controlSet.push(el)
} else if (el && this.el && el !== this.el) {
this.el.innerHTML = '';
[].slice.call(el.childNodes).forEach(child => this.el.appendChild(child))
}
this.renderedOnce = true
}}
>
{children}
</div>,
document.createElement('div')
)
},
render() {
return <noscript />
},
})
export default MapControl This plays nicely when React needs to re-render or unmount controls |
@PsychicNoodles @ashtonwar this could be easily done in 6.0.0 since we wrote it. Would you like to submit a PR? Also, 6.0.0 is released on npm beta tag now. We also have a new demo page. Feel free to try it: |
@ashtonsix this was a lifesaver for me. For anyone that needs it, here is a tweak of it implemented in ES6, and using the same internal conventions for referencing the map via the context api so that you don't have to pass in a reference to it. import React from 'react';
import PropTypes from 'prop-types';
import { render } from 'react-dom';
import { MAP } from 'react-google-maps/src/lib/constants';
class MapControl extends React.Component {
static contextTypes = {
[MAP]: PropTypes.object
}
static propTypes = {
controlPosition: PropTypes.number
}
static defaultProps = {
controlPosition: google.maps.ControlPosition.TOP_LEFT
}
componentDidMount() {
this.map = this.context[MAP];
this._render();
}
componentDidUpdate() {
this._render();
}
componentWillUnmount() {
const {controlPosition} = this.props;
const index = this.map.controls[controlPosition].getArray().indexOf(this.el);
this.map.controls[controlPosition].removeAt(index);
}
_render() {
const {controlPosition, children} = this.props;
render(
<div ref={el => {
if (!this.renderedOnce) {
this.el = el;
this.map.controls[controlPosition].push(el);
} else if (el && this.el && el !== this.el) {
this.el.innerHTML = '';
[].slice.call(el.childNodes).forEach(child => this.el.appendChild(child));
}
this.renderedOnce = true;
}}>
{children}
</div>,
document.createElement('div')
);
}
render() {
return <noscript />;
}
} |
@jamesmfriedman Bless you. This was the only way I could get this working properly without directly interacting with the scary |
If inside
UPDATE: |
Could any one of you submit a PR? LOL |
@tomchentw - have you considered creating a PR yourself? |
Is @jamesmfriedman way the go to way of adding custom controls at the moment? I tried to look for a custom controls but could not find it in the docs I am receiving a |
@quachsimon you have to have google maps added via a script tag, or explicitly import it |
How would I explicitly import it in the file? |
Is this still not possible? I'll admit that I'm still learning React and this library, but I've created the following based on the code for existing components: import canUseDOM from 'can-use-dom';
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import { componentWillUnmount } from 'react-google-maps/lib/utils/MapChildHelper';
import { MAP } from 'react-google-maps/lib/constants';
/**
* A wrapper around any component as a control on the map
*
* @see https://developers.google.com/maps/documentation/javascript/reference/3.exp/#control
*/
export class Control extends PureComponent {
static propTypes = {
/**
* Where to put `<Control>` inside a `<GoogleMap>`
*
* @example google.maps.ControlPosition.TOP_LEFT
* @type number
*/
controlPosition: PropTypes.number
}
static contextTypes = {
[MAP]: PropTypes.object,
}
componentWillMount() {
if (!canUseDOM || this.containerElement) {
return;
}
this.containerElement = document.createElement('div');
this.handleRenderChildToContainerElement();
if (React.version.match(/^16/)) {
return;
}
}
componentDidMount() {
this.handleMountAtControlPosition();
}
componentWillUpdate(nextProp) {
if (this.props.controlPosition !== nextProp.controlPosition) {
this.handleUnmountAtControlPosition();
}
}
componentDidUpdate(prevProps) {
if (this.props.children !== prevProps.children) {
this.handleRenderChildToContainerElement();
}
if (this.props.controlPosition !== prevProps.controlPosition) {
this.handleMountAtControlPosition();
}
}
componentWillUnmount() {
componentWillUnmount(this);
this.handleUnmountAtControlPosition();
if (React.version.match(/^16/)) {
return;
}
if (this.containerElement) {
ReactDOM.unmountComponentAtNode(this.containerElement);
this.containerElement = null;
}
}
handleRenderChildToContainerElement() {
if (React.version.match(/^16/)) {
return;
}
ReactDOM.unstable_renderSubtreeIntoContainer(this, React.Children.only(this.props.children), this.containerElement);
}
handleMountAtControlPosition() {
if (isValidControlPosition(this.props.controlPosition)) {
this.mountControlIndex = -1 + this.context[MAP].controls[this.props.controlPosition].push(
this.containerElement.firstChild);
}
}
handleUnmountAtControlPosition() {
if (isValidControlPosition(this.props.controlPosition)) {
const child = this.context[MAP].controls[this.props.controlPosition].removeAt(this.mountControlIndex);
if (child !== undefined) {
this.containerElement.appendChild(child);
}
}
}
render() {
if (React.version.match(/^16/)) {
return ReactDOM.createPortal(React.Children.only(this.props.children), this.containerElement);
}
return false;
}
}
export default Control;
const isValidControlPosition = _.isNumber; It seems like overkill to me, but it works. |
Currently, it is not entirely clear how to add custom control elements to the map. You can dig through the
SearchBox
andSearchBoxCreator
source to figure it out, but that seems like an unnecessary amount of work for potential devs when a working implementation is quite simple (plus, it has some intricacies due to using aninput
element).Example Implementation
(this can be submitted as a PR as well, but it's very short and simple)
Example Usage
The text was updated successfully, but these errors were encountered: