Skip to content

Commit

Permalink
split initialCameraProps into multiple props
Browse files Browse the repository at this point in the history
  • Loading branch information
usefulthink committed Feb 7, 2024
1 parent 4a8214b commit 5cbcb5b
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 69 deletions.
51 changes: 19 additions & 32 deletions docs/api-reference/components/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,25 @@ const App = () => (
);
```

## Controlled and Uncontrolled modes
## Controlled and Uncontrolled Props

The map can operate in three different modes: uncontrolled mode, controlled
mode, (both very similar to form-elements in React), and externally
controlled mode.
The props controlling the camera parameters for the map (center, zoom,
heading and tilt) can all be specified via controlled or uncontrolled values.
For example, the center of the map can be specified via either `center` or
`defaultCenter`. This can even be mixed for the different parameters (for
example, only the zoom value is controlled, while others are free).

### Uncontrolled mode

The simplest mode can be used if there is no need
for much integration of the map with the rest of your application.
In this mode, the map just receives the `initialCameraProps` and allows
full user-control after that.

In this mode (i.e., as long as `initialCameraProps` is present in the props),
the other camera props will be ignored completely.
As is the case with React form elements, the default-values will only be
applied when the map is first initialized, while the regular parameters will
make sure the map stays synchronized with the value specified.

```tsx
const INITIAL_CAMERA = {
center: {lat: 40.7, lng: -74},
zoom: 12
};

const UncontrolledMap = () => {
return <Map initialCameraProps={INITIAL_CAMERA}></Map>;
return <Map defaultCenter={{lat: 40.7, lng: -74}} defaultZoom={12}></Map>;
};
```

### Controlled mode

In this mode, the map will always exactly reflect the
When only controlled props are used, the map will always exactly reflect the
values specified for the camera parameters. When interactions occur, the new
camera parameters will be published with a `cameraChanged` event and the
application can use them to update the values passed to the props of the map.
Expand Down Expand Up @@ -79,7 +68,7 @@ anything not specified in the camera props.
## Props

The `MapProps` type extends the [`google.maps.MapOptions` interface]
[gmp-map-options] and includes all possible options available for a Google
[gmp-map-options] and includes all possible options available for a Google
Map as props.

The most important of these options are also listed below along with the
Expand Down Expand Up @@ -130,9 +119,6 @@ style-prop is no longer applied.

Coordinates for the center of the map.

The Google Maps API Documentation [has some more information on this topic]
[gmp-coordinates].

#### `zoom`: number

The initial resolution at which to display the map.
Expand All @@ -145,6 +131,9 @@ is approximately:
- `15`: Streets
- `20`: Buildings

The Google Maps API Documentation [has some more information on this topic].
[gmp-coordinates].

#### `heading`: number

The heading of the map in degrees, measured clockwise from cardinal direction
Expand All @@ -162,13 +151,11 @@ The allowed values are restricted depending on the zoom level of the map:
- between 10 and 15.5, it is a piecewise linear interpolation
([see here][get-max-tilt] for details)

#### `initialCameraProps`: MapCameraProps
#### `defaultCenter`, `defaultZoom`, `defaultHeading`, `defaultTilt`

The initial state of the camera. This can be specified to leave the map
component in uncontrolled mode. [See above](#uncontrolled-mode) for more
information about the uncontrolled mode. The `MapCameraProps` type is just
an object with the four properties `center`, `zoom`, `heading` and `tilt` as
described above.
The initial state of the camera. This can be used to leave the map
component in uncontrolled mode. When both a default-value and a controlled
value are present for a parameter, the controlled value takes precedence.

#### `initialBounds`: [google.maps.LatLngBoundsLiteral][gmp-llb]

Expand Down
4 changes: 2 additions & 2 deletions examples/_template/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const API_KEY =
const App = () => (
<APIProvider apiKey={API_KEY}>
<Map
zoom={3}
center={{lat: 22.54992, lng: 0}}
defaultCenter={{lat: 22.54992, lng: 0}}
defaultZoom={3}
gestureHandling={'greedy'}
disableDefaultUI={true}
/>
Expand Down
4 changes: 2 additions & 2 deletions examples/basic-map/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const API_KEY =
const App = () => (
<APIProvider apiKey={API_KEY}>
<Map
zoom={3}
center={{lat: 22.54992, lng: 0}}
defaultZoom={3}
defaultCenter={{lat: 22.54992, lng: 0}}
gestureHandling={'greedy'}
disableDefaultUI={true}
/>
Expand Down
4 changes: 2 additions & 2 deletions examples/change-map-styles/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ const App = () => {
return (
<APIProvider apiKey={API_KEY}>
<Map
defaultCenter={{lat: 22, lng: 0}}
defaultZoom={3}
mapId={mapConfig.mapId}
mapTypeId={mapConfig.mapTypeId}
center={{lat: 22, lng: 0}}
zoom={3}
styles={mapConfig.styles}
gestureHandling={'greedy'}
disableDefaultUI={true}>
Expand Down
4 changes: 2 additions & 2 deletions examples/deckgl-overlay/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const App = () => {
return (
<APIProvider apiKey={API_KEY}>
<Map
center={{lat: 37.74, lng: -122.4}}
zoom={11}
defaultCenter={{lat: 37.74, lng: -122.4}}
defaultZoom={11}
mapId={'4f6dde3310be51d7'}
gestureHandling={'greedy'}
disableDefaultUI={true}>
Expand Down
6 changes: 5 additions & 1 deletion examples/directions/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ const API_KEY =

const App = () => (
<APIProvider apiKey={API_KEY}>
<Map center={{lat: 43.65, lng: -79.38}} zoom={9} fullscreenControl={false}>
<Map
defaultCenter={{lat: 43.65, lng: -79.38}}
defaultZoom={9}
gestureHandling={'greedy'}
fullscreenControl={false}>
<Directions />
</Map>
</APIProvider>
Expand Down
4 changes: 2 additions & 2 deletions examples/geometry/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ const App = () => {
return (
<APIProvider apiKey={API_KEY}>
<Map
zoom={10}
center={INITIAL_CENTER}
defaultCenter={INITIAL_CENTER}
defaultZoom={10}
gestureHandling={'greedy'}
disableDefaultUI={true}>
<Marker
Expand Down
4 changes: 3 additions & 1 deletion examples/map-control/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ const App = () => {

return (
<APIProvider apiKey={API_KEY}>
{/* note that we can also use a mix of controlled (zoom) an
uncontrolled (center) properties here */}
<Map
disableDefaultUI={true}
gestureHandling={'greedy'}
mapId={'49ae42fed52588c3'}
center={center}
defaultCenter={center}
zoom={zoom}
onZoomChanged={ev => setZoom(ev.detail.zoom)}>
<MapControl position={ControlPosition.TOP_LEFT}>
Expand Down
6 changes: 4 additions & 2 deletions examples/marker-clustering/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ const App = () => (
<APIProvider apiKey={API_KEY}>
<Map
mapId={'bf51a910020fa25a'}
center={{lat: 43.64, lng: -79.41}}
zoom={10}>
defaultCenter={{lat: 43.64, lng: -79.41}}
defaultZoom={10}
gestureHandling={'greedy'}
disableDefaultUI>
<Markers points={trees} />
</Map>
</APIProvider>
Expand Down
6 changes: 3 additions & 3 deletions examples/markers-and-infowindows/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const App = () => {
<APIProvider apiKey={API_KEY} libraries={['marker']}>
<Map
mapId={'bf51a910020fa25a'}
zoom={3}
center={{lat: 12, lng: 0}}
defaultZoom={3}
defaultCenter={{lat: 12, lng: 0}}
gestureHandling={'greedy'}
disableDefaultUI={true}>
disableDefaultUI>
{/* simple marker */}
<Marker
position={{lat: 10, lng: 10}}
Expand Down
14 changes: 7 additions & 7 deletions src/components/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ export type MapProps = google.maps.MapOptions &
* Indicates that the map will be controlled externally. Disables all controls provided by the map itself.
*/
controlled?: boolean;

defaultCenter?: google.maps.LatLngLiteral;
defaultZoom?: number;
defaultHeading?: number;
defaultTilt?: number;
/**
* The initial parameters for the camera. If specified, the map will be in uncontrolled mode and
* will ignore the regular camera parameters (center/zoom/heading/tilt).
*/
initialCameraProps?: MapCameraProps;
/**
* Alternative way to specify the initialCameraProps as geographic region that should be visible
* Alternative way to specify the default camera props as a geographic region that should be fully visible
*/
initialBounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
defaultBounds?: google.maps.LatLngBoundsLiteral;
};

export const Map = (props: PropsWithChildren<MapProps>) => {
Expand Down
4 changes: 0 additions & 4 deletions src/components/map/use-map-camera-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ export function useMapCameraParams(
useLayoutEffect(() => {
if (!map) return;

// when the map was configured with an initialCameraProps or initialBounds,
// we skip all camera updates here
if (mapProps.initialCameraProps || mapProps.initialBounds) return;

const nextCamera: google.maps.CameraOptions = {};
let needsUpdate = false;

Expand Down
28 changes: 19 additions & 9 deletions src/components/map/use-map-instance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Ref, useEffect, useState} from 'react';
import {Ref, useEffect, useRef, useState} from 'react';

import {MapProps} from '../map';
import {APIProviderContextValue} from '../api-provider';
Expand All @@ -23,12 +23,24 @@ export function useMapInstance(

const {
id,
initialBounds,
initialCameraProps,
defaultBounds,
defaultCenter,
defaultZoom,
defaultHeading,
defaultTilt,

...mapOptions
} = props;

// apply default camera props if available and not overwritten by controlled props
if (!mapOptions.center && defaultCenter) mapOptions.center = defaultCenter;
if (!mapOptions.zoom && Number.isFinite(defaultZoom))
mapOptions.zoom = defaultZoom;
if (!mapOptions.heading && Number.isFinite(defaultHeading))
mapOptions.heading = defaultHeading;
if (!mapOptions.tilt && Number.isFinite(defaultTilt))
mapOptions.tilt = defaultTilt;

// create the map instance and register it in the context
useEffect(
() => {
Expand All @@ -40,13 +52,11 @@ export function useMapInstance(
setMap(newMap);
addMapInstance(newMap, id);

if (initialBounds) {
newMap.fitBounds(initialBounds);
if (defaultBounds) {
newMap.fitBounds(defaultBounds);
}

if (initialCameraProps) {
newMap.setOptions(initialCameraProps);
}
// FIXME: When the mapId is changed, we need to maintain the current camera params.

return () => {
if (!container || !apiIsLoaded) return;
Expand All @@ -60,7 +70,7 @@ export function useMapInstance(
},

// some dependencies are ignored in the list below:
// - initialBounds and initialCameraProps will only be used once, and
// - defaultBounds and the default* camera props will only be used once, and
// changes should be ignored
// - mapOptions has special hooks that take care of updating the options
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down

0 comments on commit 5cbcb5b

Please sign in to comment.