-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(infowindow): InfoWindow overhaul (#335)
A thorough overhaul of the InfoWindow implementation, tests and documentation. fix(infowindow): removed unneeded dependency in infowindow hooks fix(infowindow): add missing cleanup for infowindow fix(infowindow): better dependency checks, using `useDeepCompareEffect` where needed feat(infowindow): add missing options and events feat(infowindow): add `className` and `style` props
- Loading branch information
1 parent
2a51f2c
commit 92854c9
Showing
5 changed files
with
588 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,206 @@ | ||
# `<InfoWindow>` Component | ||
|
||
React component to display an [Info Window](https://developers.google.com/maps/documentation/javascript/reference/info-window) instance. | ||
[InfoWindows][gmp-infowindow] are small, temporary overlays on the map that | ||
are typically used to display additional bits of information for locations on | ||
the map – for example, to add a label or image to a marker. They can be | ||
freely positioned on the map, or they can be "anchored" to a marker. | ||
|
||
## Usage | ||
When an `InfoWindow` is shown, the map will make sure to reposition the viewport | ||
such that the InfoWindow is well visible within the map container. | ||
|
||
Info Windows can either be displayed alone, or in connection with a Marker | ||
that will be used as an anchor. The content of the InfoWindow can either be | ||
text or any JSX and is specified as the children of the InfoWindow | ||
component. | ||
InfoWindows always use the same well-known styling and are limited in how much | ||
their look and feel can be customized. Any JSX element added to the | ||
InfoWindow component as children will get rendered into the content of the | ||
InfoWindow. | ||
|
||
Under the hood this is using the `google.maps.InfoWindow` class. The default | ||
InfoWindow does come with a close-button that can't easily be removed or | ||
controlled via the API. This means that the visibility of the infowindow | ||
cannot be fully controlled by your application. To keep your state in sync | ||
with the map, you can listen for the `onCloseClick` event. | ||
:::note | ||
|
||
### Single Info Window implementation | ||
The rendered InfoWindow includes a close-button that can't be removed or | ||
controlled via the Google Maps API. This means that the application can't | ||
fully control the visibility of the InfoWindow. | ||
|
||
```tsx | ||
import React from 'react'; | ||
import {APIProvider, Map, InfoWindow} from '@vis.gl/react-google-maps'; | ||
|
||
const App = () => ( | ||
<APIProvider apiKey={'Your API key here'}> | ||
<Map zoom={12} center={{lat: 53.54992, lng: 10.00678}}> | ||
<InfoWindow position={{lat: 53.54992, lng: 10.00678}}> | ||
Hello World! | ||
</InfoWindow> | ||
</Map> | ||
</APIProvider> | ||
); | ||
To keep your state in sync with the map, you have to provide a listener for the | ||
`onClose` event so the application knows when then InfoWindow was closed by | ||
the map or the user. | ||
|
||
export default App; | ||
``` | ||
::: | ||
|
||
### Marker with JSX implementation | ||
:::tip | ||
|
||
```tsx | ||
import React from 'react'; | ||
import { | ||
APIProvider, | ||
Map, | ||
Marker, | ||
useMarkerRef | ||
} from '@vis.gl/react-google-maps'; | ||
If you need more control over an InfoWindow than can be offered by the | ||
`InfoWindow` component, you can use the [`AdvancedMarker`](./advanced-marker.md) | ||
component with html-content to create a custom implementation. | ||
|
||
::: | ||
|
||
## Usage | ||
|
||
### Minimal Example | ||
|
||
const App = () => { | ||
const [markerRef, marker] = useMarkerRef(); | ||
In this example, the InfoWindow will be initially shown when the map is | ||
rendered, but the user can close it and there wouldn't be a way to get it back. | ||
|
||
```tsx | ||
const MapWithInfoWindow = () => { | ||
return ( | ||
<APIProvider apiKey={'Your API key here'}> | ||
<Map zoom={12} center={{lat: 53.54992, lng: 10.00678}}> | ||
<Marker ref={markerRef} position={{lat: 53.54992, lng: 10.00678}} /> | ||
|
||
<InfoWindow anchor={marker}> | ||
<h2>Hello everyone!</h2> | ||
<p>This is an Info Window</p> | ||
<img src="..." /> | ||
</InfoWindow> | ||
</Map> | ||
</APIProvider> | ||
<Map {...mapProps}> | ||
<InfoWindow position={infoWindowPosition}> | ||
The content of the info window is here. | ||
</InfoWindow> | ||
</Map> | ||
); | ||
}; | ||
|
||
export default App; | ||
``` | ||
|
||
**Note**: The position prop of the InfoWindow will be ignored when an anchor is specified. | ||
### Infowindow Attached to Marker | ||
|
||
### Advanced Marker View implementation | ||
A more typical use-case is to have an InfoWindow shown on click for a marker. | ||
One way to implement this is to write a custom component | ||
`MarkerWithInfoWindow` that can then be added to any `Map`. | ||
|
||
```tsx | ||
import React, {FunctionComponent} from 'react'; | ||
import { | ||
APIProvider, | ||
Map, | ||
AdvancedMarker, | ||
useAdvancedMarkerRef | ||
} from '@vis.gl/react-google-maps'; | ||
|
||
const App = () => { | ||
const MarkerWithInfoWindow = ({position}) => { | ||
// `markerRef` and `marker` are needed to establish the connection between | ||
// the marker and infowindow (if you're using the Marker component, you | ||
// can use the `useMarkerRef` hook instead). | ||
const [markerRef, marker] = useAdvancedMarkerRef(); | ||
const [infowindowShown, setInfowindowShown] = useState(false); | ||
|
||
const toggleInfoWindow = () => | ||
setInfowindowShown(previousState => !previousState); | ||
const [infoWindowShown, setInfoWindowShown] = useState(false); | ||
|
||
const closeInfoWindow = () => setInfowindowShown(false); | ||
// clicking the marker will toggle the infowindow | ||
const handleMarkerClick = useCallback(() => | ||
setInfoWindowShown(isShown => !isShown) | ||
); | ||
|
||
// if the maps api closes the infowindow, we have to synchronize our state | ||
const handleClose = useCallback(() => setInfoWindowShown(false)); | ||
|
||
return ( | ||
<APIProvider apiKey={'Your API key here'}> | ||
<Map zoom={12} center={{lat: 53.54992, lng: 10.00678}} mapId={'<Your custom MapId here>'}> | ||
<AdvancedMarker | ||
ref={markerRef} | ||
position={{lat: 53.54992, lng: 10.00678}} | ||
onClick={toggleInfoWindow} | ||
/> | ||
|
||
{infowindowShown && ( | ||
<InfoWindow anchor={marker} onCloseClick={closeInfoWindow}> | ||
You can drag and drop me. | ||
</InfoWindow> | ||
)} | ||
</Map> | ||
</APIProvider> | ||
<> | ||
<AdvancedMarker | ||
ref={markerRef} | ||
position={position} | ||
onClick={handleMarkerClick} | ||
/> | ||
|
||
{infoWindowShown && ( | ||
<InfoWindow anchor={marker} onClose={handleClose}> | ||
<h2>InfoWindow content!</h2> | ||
<p>Some arbitrary html to be rendered into the InfoWindow.</p> | ||
</InfoWindow> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default App; | ||
``` | ||
|
||
## Props | ||
|
||
The InfoWindowProps interface extends the [google.maps.InfoWindowOptions interface](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindowOptions) and includes all possible options available for a Google Maps Info Window. | ||
The InfoWindowProps interface roughly extends the [`google.maps.InfoWindowOptions` | ||
interface][gmp-infowindow-options] and includes all the options available for a | ||
InfoWindow as props. All supported options are listed below. | ||
|
||
- `onCloseClick` adds the event listener 'closeclick' to the info infowindow | ||
- `anchor` a Marker or AdvancedMarker instance to be used as an anchor | ||
### Required | ||
|
||
```tsx | ||
export type InfoWindowProps = google.maps.InfoWindowOptions & { | ||
onCloseClick?: () => void; | ||
anchor?: google.maps.Marker | google.maps.marker.AdvancedMarkerElement | null; | ||
}; | ||
``` | ||
There are no strictly required props for the InfoWindow component, but it is | ||
required to set either a `position` or an `anchor` to show the infowindow. | ||
|
||
### General Props | ||
|
||
#### `position`: google.maps.LatLngLiteral | ||
|
||
The LatLng at which to display this InfoWindow. | ||
|
||
:::note | ||
|
||
When an `anchor` is specified, the `position` prop will be ignored. | ||
|
||
::: | ||
|
||
#### `anchor`: google.maps.Marker | google.maps.marker.AdvancedMarkerElement | ||
|
||
A Marker or AdvancedMarker instance to be used as an anchor. If specified, the | ||
InfoWindow will be positioned at the top-center of the anchor. | ||
|
||
#### `zIndex`: number | ||
|
||
All InfoWindows are displayed on the map in order of their zIndex, with | ||
higher values displaying in front of InfoWindows with lower values. By | ||
default, InfoWindows are displayed according to their latitude, with | ||
InfoWindows of lower latitudes appearing in front of InfoWindows at higher | ||
latitudes. InfoWindows are always displayed in front of markers. | ||
|
||
#### `pixelOffset`: [number, number] | ||
|
||
The offset, in pixels, from the tip of the info window to the point on the | ||
map at whose geographical coordinates the info window is anchored. | ||
If an InfoWindow is opened with an anchor, the `pixelOffset` will be | ||
calculated from the anchor's top/center. | ||
|
||
#### `disableAutoPan`: boolean | ||
|
||
Disable panning the map to make the InfoWindow fully visible when it opens. | ||
|
||
#### `shouldFocus`: boolean | ||
|
||
Whether focus should be moved inside the InfoWindow when it is opened. When | ||
this property isn't set, a heuristic is used to decide whether focus should | ||
be moved. | ||
|
||
It is recommended to explicitly set this property to fit your needs as the | ||
heuristic is subject to change and may not work well for all use cases. | ||
|
||
### Content Props | ||
|
||
#### `className`: string | ||
|
||
A className to be assigned to the topmost element in the infowindow content. | ||
|
||
#### `style`: [CSSProperties][react-dev-styling] | ||
|
||
A style declaration to be added to the topmost element in the infowindow | ||
content. This works exactly as the style property for any other | ||
html element. | ||
|
||
#### `ariaLabel`: string | ||
|
||
AriaLabel to assign to the InfoWindow. | ||
|
||
#### `minWidth`: number | ||
|
||
Minimum width of the InfoWindow, regardless of the content's width. When | ||
using this property, it is strongly recommended to set the minWidth to a | ||
value less than the width of the map (in pixels). | ||
|
||
:::note | ||
|
||
The `minWidth` can't be changed while the InfoWindow is open. | ||
|
||
::: | ||
|
||
#### `maxWidth`: number | ||
|
||
Maximum width of the InfoWindow, regardless of content's width. | ||
|
||
:::note | ||
|
||
The `minWidth` can't be changed while the InfoWindow is open. | ||
|
||
::: | ||
|
||
### Events | ||
|
||
#### `onClose`: () => void | ||
|
||
This event is fired whenever the InfoWindow closes. This could be from | ||
unmouting the InfoWindow component, pressing the escape key to close the | ||
InfoWindow, or clicking the close button or removing the marker the | ||
InfoWindow was anchored to. | ||
|
||
#### `onCloseClick`: () => void | ||
|
||
This event is fired when the close button was clicked. | ||
|
||
To see an InfoWindow on the map, either the `position` property or the anchor needs to be set. | ||
[gmp-infowindow]: https://developers.google.com/maps/documentation/javascript/infowindows | ||
[gmp-infowindow-options]: https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindowOptions | ||
[react-dev-styling]: https://react.dev/reference/react-dom/components/common#applying-css-styles |
Oops, something went wrong.