mirror of
https://github.com/visgl/react-map-gl.git
synced 2026-01-25 16:02:50 +00:00
[v7] utility hooks (#1663)
This commit is contained in:
parent
55ce87d3f7
commit
3f200a0bfe
21
docs/api-reference/map-provider.md
Normal file
21
docs/api-reference/map-provider.md
Normal file
@ -0,0 +1,21 @@
|
||||
# MapProvider
|
||||
|
||||
A [Context.Provider](https://reactjs.org/docs/context.html#contextprovider) that facilitates map operations outside of the component that directly renders a [Map](/docs/api-reference/map.md).
|
||||
|
||||
The component should wrap all nodes in which you may want to access the maps:
|
||||
|
||||
```js
|
||||
import {MapProvider} from 'react-map-gl';
|
||||
|
||||
function Root() {
|
||||
return (
|
||||
<MapProvider>
|
||||
{
|
||||
// Application tree, somewhere one or more <Map /> component(s) are rendered
|
||||
}
|
||||
</MapProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See [useMap](/docs/api-reference/use-map.md) for more information.
|
||||
@ -24,11 +24,8 @@ function App() {
|
||||
|
||||
## Methods
|
||||
|
||||
#### `getMap()`
|
||||
The following methods are accessible via a [React ref](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or the [useMap](/docs/api-reference/use-map.md) hook.
|
||||
|
||||
Returns the underlying [Map](https://docs.mapbox.com/mapbox-gl-js/api/map/) instance.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import * as React from 'react';
|
||||
@ -46,6 +43,13 @@ function App() {
|
||||
}
|
||||
```
|
||||
|
||||
#### `getMap()`
|
||||
|
||||
Returns the underlying [Map](https://docs.mapbox.com/mapbox-gl-js/api/map/) instance.
|
||||
|
||||
#### `getViewState()`
|
||||
|
||||
Returns the current view state of the map.
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
64
docs/api-reference/use-control.md
Normal file
64
docs/api-reference/use-control.md
Normal file
@ -0,0 +1,64 @@
|
||||
# useControl
|
||||
|
||||
The `useControl` hook is used to create React wrappers for custom map controls.
|
||||
|
||||
```js
|
||||
import MapboxDraw from '@mapbox/mapbox-gl-draw';
|
||||
import {Map, useControl} from 'react-map-gl';
|
||||
|
||||
function DrawControl(props: DrawControlProps) {
|
||||
useControl(() => new MapboxDraw(props), {
|
||||
position: props.position
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Map
|
||||
initialViewState={{
|
||||
longitude: -122.4,
|
||||
latitude: 37.8,
|
||||
zoom: 14
|
||||
}}
|
||||
mapStyle="mapbox://styles/mapbox/satellite-v9"
|
||||
>
|
||||
<DrawControl
|
||||
position="top-left"
|
||||
displayControlsDefault={false}
|
||||
controls={{
|
||||
polygon: true,
|
||||
trash: true
|
||||
}}
|
||||
/>
|
||||
</Map>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See a full example [here](/examples/draw-polygon).
|
||||
|
||||
## Signature
|
||||
|
||||
```js
|
||||
useControl(onCreate: () => IControl, options?: {
|
||||
position?: ControlPosition;
|
||||
onAdd?: (map: MapboxMap) => void;
|
||||
onRemove?: (map: MapboxMap) => void;
|
||||
}): IControl
|
||||
```
|
||||
|
||||
The hook creates an [IControl](https://docs.mapbox.com/mapbox-gl-js/api/markers/#icontrol) instance, adds it to the map when it's available, and removes it upon unmount.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `onCreate` (Function) - called to create an instance of the control.
|
||||
- `options` (object)
|
||||
+ `position` ('top-left' | 'top-right' | 'bottom-left' | 'bottom-right') - control position relative to the map
|
||||
+ `onAdd` (Function) - called after the control is added to a map
|
||||
+ `onRemove` (Function) - called before the control is removed from a map
|
||||
|
||||
Returns:
|
||||
|
||||
The control instance.
|
||||
37
docs/api-reference/use-map.md
Normal file
37
docs/api-reference/use-map.md
Normal file
@ -0,0 +1,37 @@
|
||||
# useMap
|
||||
|
||||
The `useMap` hook, used with the [MapProvider](/docs/api-reference/map-provider.md), helps an app to perform map operations outside of the component that directly renders a [Map](/docs/api-reference/map.md).
|
||||
|
||||
|
||||
```js
|
||||
import {MapProvider, Map, useMap} from 'react-map-gl';
|
||||
|
||||
function Root() {
|
||||
return (
|
||||
<MapProvider>
|
||||
<Map id="myMapA" ... />
|
||||
<Map id="myMapB" ... />
|
||||
<NavigateButton />
|
||||
</MapProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function NavigateButton() {
|
||||
const {myMapA, myMapB} = useMap();
|
||||
|
||||
const onClick = () => {
|
||||
myMapA.flyTo({center: [-122.4, 37.8]});
|
||||
myMapB.flyTo({center: [-74, 40.7]});
|
||||
};
|
||||
|
||||
return <button onClick={onClick}>Go</button>;
|
||||
}
|
||||
```
|
||||
|
||||
See a full example [here](/examples/get-started/hook).
|
||||
|
||||
## Signature
|
||||
|
||||
`useMap(): {[id: string]: MapRef}`
|
||||
|
||||
The hook returns an object that contains all mounted maps under the closest `MapProvider`. The keys are each map's [id](/docs/api-reference/map.md#prperties) and the values are the [ref object](/docs/api-reference/map.md#methods).
|
||||
@ -18,15 +18,14 @@ export default function App() {
|
||||
const feature = event.features[0];
|
||||
const clusterId = feature.properties.cluster_id;
|
||||
|
||||
const map = mapRef.current.getMap();
|
||||
const mapboxSource = map.getSource('earthquakes') as GeoJSONSource;
|
||||
const mapboxSource = mapRef.current.getSource('earthquakes') as GeoJSONSource;
|
||||
|
||||
mapboxSource.getClusterExpansionZoom(clusterId, (err, zoom) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
map.easeTo({
|
||||
mapRef.current.easeTo({
|
||||
center: feature.geometry.coordinates,
|
||||
zoom,
|
||||
duration: 500
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Example: Draw Polygon
|
||||
|
||||
Demonstrates how to use [react-map-gl-draw](https://github.com/uber/nebula.gl/tree/master/modules/react-map-gl-draw) to draw polygons with react-map-gl.
|
||||
This app reproduces Mapbox's [Draw a polygon and calculate its area](https://docs.mapbox.com/mapbox-gl-js/example/mapbox-gl-draw/) example.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: #000;
|
||||
}
|
||||
#map {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
max-width: 320px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
@ -1,17 +1,41 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='UTF-8' />
|
||||
<title>react-map-gl Example</title>
|
||||
<link rel="stylesheet" type="text/css" href="app.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<!--use mapbox button icon css-->
|
||||
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.css" type="text/css">
|
||||
<script src='app.js'></script>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
App.renderToDom(document.getElementById('map'));
|
||||
</script>
|
||||
<head>
|
||||
<meta charset='UTF-8' />
|
||||
<title>react-map-gl Example</title>
|
||||
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.3.0/mapbox-gl-draw.css" type="text/css">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
#map {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
max-width: 320px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src='app.js'></script>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
App.renderToDom(document.getElementById('map'));
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@ -1,27 +1,25 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --progress --hot --open",
|
||||
"start-local": "webpack-dev-server --env.local --progress --hot --open"
|
||||
"start-local": "webpack-dev-server --env local --progress --hot --open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@turf/area": "^6.0.1",
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0",
|
||||
"react-map-gl": "^6.0.0",
|
||||
"react-map-gl-draw": "^0.21.0",
|
||||
"styled-components": "^4.3.2"
|
||||
"@mapbox/mapbox-gl-draw": "^1.3.0",
|
||||
"@turf/area": "^6.5.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react-map-gl": "^7.0.0",
|
||||
"mapbox-gl": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"webpack": "^4.20.0",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@turf/difference": "6.0.1"
|
||||
"ts-loader": "^9.0.0",
|
||||
"typescript": "^4.0.0",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.0",
|
||||
"webpack-dev-server": "^4.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import {useState, useRef, useCallback} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL from 'react-map-gl';
|
||||
import {Editor, DrawPolygonMode, EditingMode} from 'react-map-gl-draw';
|
||||
|
||||
import ControlPanel from './control-panel';
|
||||
import {getFeatureStyle, getEditHandleStyle} from './style';
|
||||
|
||||
const TOKEN = ''; // Set your mapbox token here
|
||||
|
||||
export default function App() {
|
||||
const [viewport, setViewport] = useState({
|
||||
longitude: -91.874,
|
||||
latitude: 42.76,
|
||||
zoom: 12
|
||||
});
|
||||
const [mode, setMode] = useState(null);
|
||||
const [selectedFeatureIndex, setSelectedFeatureIndex] = useState(null);
|
||||
const editorRef = useRef(null);
|
||||
|
||||
const onSelect = useCallback(options => {
|
||||
setSelectedFeatureIndex(options && options.selectedFeatureIndex);
|
||||
}, []);
|
||||
|
||||
const onDelete = useCallback(() => {
|
||||
if (selectedFeatureIndex !== null && selectedFeatureIndex >= 0) {
|
||||
editorRef.current.deleteFeatures(selectedFeatureIndex);
|
||||
}
|
||||
}, [selectedFeatureIndex]);
|
||||
|
||||
const onUpdate = useCallback(({editType}) => {
|
||||
if (editType === 'addFeature') {
|
||||
setMode(new EditingMode());
|
||||
}
|
||||
}, []);
|
||||
|
||||
const drawTools = (
|
||||
<div className="mapboxgl-ctrl-top-left">
|
||||
<div className="mapboxgl-ctrl-group mapboxgl-ctrl">
|
||||
<button
|
||||
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_polygon"
|
||||
title="Polygon tool (p)"
|
||||
onClick={() => setMode(new DrawPolygonMode())}
|
||||
/>
|
||||
<button
|
||||
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
|
||||
title="Delete"
|
||||
onClick={onDelete}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const features = editorRef.current && editorRef.current.getFeatures();
|
||||
const selectedFeature =
|
||||
features && (features[selectedFeatureIndex] || features[features.length - 1]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MapGL
|
||||
{...viewport}
|
||||
width="100%"
|
||||
height="100%"
|
||||
mapStyle="mapbox://styles/mapbox/satellite-v9"
|
||||
mapboxApiAccessToken={TOKEN}
|
||||
onViewportChange={setViewport}
|
||||
>
|
||||
<Editor
|
||||
ref={editorRef}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
clickRadius={12}
|
||||
mode={mode}
|
||||
onSelect={onSelect}
|
||||
onUpdate={onUpdate}
|
||||
editHandleShape={'circle'}
|
||||
featureStyle={getFeatureStyle}
|
||||
editHandleStyle={getEditHandleStyle}
|
||||
/>
|
||||
{drawTools}
|
||||
</MapGL>
|
||||
<ControlPanel polygon={selectedFeature} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderToDom(container) {
|
||||
render(<App />, container);
|
||||
}
|
||||
64
examples/draw-polygon/src/app.tsx
Normal file
64
examples/draw-polygon/src/app.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import * as React from 'react';
|
||||
import {useState, useCallback} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import Map from 'react-map-gl';
|
||||
|
||||
import DrawControl from './draw-control';
|
||||
import ControlPanel from './control-panel';
|
||||
|
||||
const TOKEN = ''; // Set your mapbox token here
|
||||
|
||||
export default function App() {
|
||||
const [features, setFeatures] = useState({});
|
||||
|
||||
const onUpdate = useCallback(
|
||||
e => {
|
||||
const newFeatures = {...features};
|
||||
for (const f of e.features) {
|
||||
newFeatures[f.id] = f;
|
||||
}
|
||||
setFeatures(newFeatures);
|
||||
},
|
||||
[features]
|
||||
);
|
||||
|
||||
const onDelete = useCallback(e => {
|
||||
const newFeatures = {...features};
|
||||
for (const f of e.features) {
|
||||
delete newFeatures[f.id];
|
||||
}
|
||||
setFeatures(newFeatures);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
initialViewState={{
|
||||
longitude: -91.874,
|
||||
latitude: 42.76,
|
||||
zoom: 12
|
||||
}}
|
||||
mapStyle="mapbox://styles/mapbox/satellite-v9"
|
||||
mapboxAccessToken={TOKEN}
|
||||
>
|
||||
<DrawControl
|
||||
position="top-left"
|
||||
displayControlsDefault={false}
|
||||
controls={{
|
||||
polygon: true,
|
||||
trash: true
|
||||
}}
|
||||
defaultMode="draw_polygon"
|
||||
onCreate={onUpdate}
|
||||
onUpdate={onUpdate}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
</Map>
|
||||
<ControlPanel polygons={Object.values(features)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderToDom(container) {
|
||||
render(<App />, container);
|
||||
}
|
||||
@ -2,14 +2,17 @@ import * as React from 'react';
|
||||
import area from '@turf/area';
|
||||
|
||||
function ControlPanel(props) {
|
||||
const polygon = props.polygon;
|
||||
const polygonArea = polygon && area(polygon);
|
||||
let polygonArea = 0;
|
||||
for (const polygon of props.polygons) {
|
||||
polygonArea += area(polygon);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="control-panel">
|
||||
<h3>Draw Polygon</h3>
|
||||
{polygon && (
|
||||
{polygonArea > 0 && (
|
||||
<p>
|
||||
{polygonArea} <br />
|
||||
{Math.round(polygonArea * 100) / 100} <br />
|
||||
square meters
|
||||
</p>
|
||||
)}
|
||||
56
examples/draw-polygon/src/draw-control.ts
Normal file
56
examples/draw-polygon/src/draw-control.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import MapboxDraw from '@mapbox/mapbox-gl-draw';
|
||||
import {useControl} from 'react-map-gl';
|
||||
|
||||
import type {MapboxMap, ControlPosition} from 'react-map-gl';
|
||||
|
||||
type ControlTypes =
|
||||
| 'point'
|
||||
| 'line_string'
|
||||
| 'polygon'
|
||||
| 'trash'
|
||||
| 'combine_features'
|
||||
| 'uncombine_features';
|
||||
|
||||
type DrawControlProps = {
|
||||
keybindings?: boolean;
|
||||
touchEnable?: boolean;
|
||||
boxSelect?: boolean;
|
||||
clickBuffer?: number;
|
||||
touchBuffer?: number;
|
||||
controls?: Partial<{[name in ControlTypes]: boolean}>;
|
||||
displayControlsDefault?: boolean;
|
||||
styles?: any;
|
||||
modes?: any;
|
||||
defaultMode?: string;
|
||||
userProperties?: boolean;
|
||||
|
||||
position?: ControlPosition;
|
||||
|
||||
onCreate?: (evt: {features: object[]}) => void;
|
||||
onUpdate?: (evt: {features: object[]; action: string}) => void;
|
||||
onDelete?: (evt: {features: object[]}) => void;
|
||||
};
|
||||
|
||||
export default function DrawControl(props: DrawControlProps) {
|
||||
useControl(() => new MapboxDraw(props), {
|
||||
position: props.position,
|
||||
onAdd: (map: MapboxMap) => {
|
||||
map.on('draw.create', props.onCreate);
|
||||
map.on('draw.update', props.onUpdate);
|
||||
map.on('draw.delete', props.onDelete);
|
||||
},
|
||||
onRemove: (map: MapboxMap) => {
|
||||
map.off('draw.create', props.onCreate);
|
||||
map.off('draw.update', props.onUpdate);
|
||||
map.off('draw.delete', props.onDelete);
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
DrawControl.defaultProps = {
|
||||
onCreate: () => {},
|
||||
onUpdate: () => {},
|
||||
onDelete: () => {}
|
||||
};
|
||||
@ -1,49 +0,0 @@
|
||||
import {RENDER_STATE} from 'react-map-gl-draw';
|
||||
|
||||
export function getEditHandleStyle({feature, state}) {
|
||||
switch (state) {
|
||||
case RENDER_STATE.SELECTED:
|
||||
case RENDER_STATE.HOVERED:
|
||||
case RENDER_STATE.UNCOMMITTED:
|
||||
return {
|
||||
fill: 'rgb(251, 176, 59)',
|
||||
fillOpacity: 1,
|
||||
stroke: 'rgb(255, 255, 255)',
|
||||
strokeWidth: 2,
|
||||
r: 7
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
fill: 'rgb(251, 176, 59)',
|
||||
fillOpacity: 1,
|
||||
stroke: 'rgb(255, 255, 255)',
|
||||
strokeWidth: 2,
|
||||
r: 5
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getFeatureStyle({feature, index, state}) {
|
||||
switch (state) {
|
||||
case RENDER_STATE.SELECTED:
|
||||
case RENDER_STATE.HOVERED:
|
||||
case RENDER_STATE.UNCOMMITTED:
|
||||
case RENDER_STATE.CLOSING:
|
||||
return {
|
||||
stroke: 'rgb(251, 176, 59)',
|
||||
strokeWidth: 2,
|
||||
fill: 'rgb(251, 176, 59)',
|
||||
fillOpacity: 0.3,
|
||||
strokeDasharray: '4,2'
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
stroke: 'rgb(60, 178, 208)',
|
||||
strokeWidth: 2,
|
||||
fill: 'rgb(60, 178, 208)',
|
||||
fillOpacity: 0.1
|
||||
};
|
||||
}
|
||||
}
|
||||
10
examples/draw-polygon/tsconfig.json
Normal file
10
examples/draw-polygon/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
||||
@ -5,33 +5,40 @@
|
||||
const resolve = require('path').resolve;
|
||||
const webpack = require('webpack');
|
||||
|
||||
const BABEL_CONFIG = {
|
||||
presets: ['@babel/env', '@babel/react'],
|
||||
plugins: ['@babel/proposal-class-properties']
|
||||
};
|
||||
|
||||
const config = {
|
||||
mode: 'development',
|
||||
|
||||
devServer: {
|
||||
static: '.'
|
||||
},
|
||||
|
||||
entry: {
|
||||
app: resolve('./src/app.js')
|
||||
app: resolve('./src/app')
|
||||
},
|
||||
|
||||
output: {
|
||||
library: 'App'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.json']
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
// Compile ES2015 using babel
|
||||
test: /\.js$/,
|
||||
test: /\.(ts|js)x?$/,
|
||||
include: [resolve('.')],
|
||||
exclude: [/node_modules/],
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: BABEL_CONFIG
|
||||
options: {
|
||||
presets: ['@babel/env', '@babel/react']
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'ts-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -39,7 +46,7 @@ const config = {
|
||||
},
|
||||
|
||||
// Optional: Enables reading mapbox token from environment variable
|
||||
plugins: [new webpack.EnvironmentPlugin(['MapboxAccessToken'])]
|
||||
plugins: [new webpack.EnvironmentPlugin({MapboxAccessToken: ''})]
|
||||
};
|
||||
|
||||
// Enables bundling against src in this repo rather than the installed version
|
||||
|
||||
@ -30,15 +30,8 @@ export default function App() {
|
||||
} = event;
|
||||
const hoveredFeature = features && features[0];
|
||||
|
||||
setHoverInfo(
|
||||
hoveredFeature
|
||||
? {
|
||||
feature: hoveredFeature,
|
||||
x,
|
||||
y
|
||||
}
|
||||
: null
|
||||
);
|
||||
// prettier-ignore
|
||||
setHoverInfo(hoveredFeature && {feature: hoveredFeature, x, y});
|
||||
}, []);
|
||||
|
||||
const data = useMemo(() => {
|
||||
|
||||
18
examples/get-started/hook/README.md
Normal file
18
examples/get-started/hook/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# react-map-gl Example: Using Map with a State Management System
|
||||
|
||||
This example shows how to use react-map-gl's Map component with the `useMap` hook.
|
||||
|
||||
## Usage
|
||||
|
||||
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `map.js`, or set a `MapboxAccessToken` environment variable in the command line.
|
||||
|
||||
```bash
|
||||
npm i
|
||||
npm run start
|
||||
```
|
||||
|
||||
To build a production version:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
18
examples/get-started/hook/app.js
Normal file
18
examples/get-started/hook/app.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* global document */
|
||||
import * as React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import {MapProvider} from 'react-map-gl';
|
||||
|
||||
import Map from './map';
|
||||
import Controls from './controls';
|
||||
|
||||
function Root() {
|
||||
return (
|
||||
<MapProvider>
|
||||
<Controls />
|
||||
<Map />
|
||||
</MapProvider>
|
||||
);
|
||||
}
|
||||
|
||||
render(<Root />, document.body.appendChild(document.createElement('div')));
|
||||
57
examples/get-started/hook/controls.js
vendored
Normal file
57
examples/get-started/hook/controls.js
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import {useCallback, useState, useEffect} from 'react';
|
||||
import {useMap} from 'react-map-gl';
|
||||
|
||||
export default function Controls() {
|
||||
const {mymap} = useMap();
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [hasError, setError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mymap) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const onMove = () => {
|
||||
const {longitude, latitude} = mymap.getViewState();
|
||||
setInputValue(`${longitude.toFixed(3)}, ${latitude.toFixed(3)}`);
|
||||
setError(false);
|
||||
};
|
||||
mymap.on('move', onMove);
|
||||
onMove();
|
||||
|
||||
return () => {
|
||||
mymap.off('move', onMove);
|
||||
};
|
||||
}, [mymap]);
|
||||
|
||||
const onChange = useCallback(evt => {
|
||||
setInputValue(evt.target.value);
|
||||
}, []);
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
const [lng, lat] = inputValue.split(',').map(Number);
|
||||
if (Math.abs(lng) <= 180 && Math.abs(lat) <= 85) {
|
||||
mymap.easeTo({
|
||||
center: [lng, lat],
|
||||
duration: 1000
|
||||
});
|
||||
} else {
|
||||
setError(true);
|
||||
}
|
||||
}, [mymap, inputValue]);
|
||||
|
||||
return (
|
||||
<div style={{padding: 12, fontFamily: 'sans-serif'}}>
|
||||
<span>MAP CENTER: </span>
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={onChange}
|
||||
style={{color: hasError ? 'red' : 'black'}}
|
||||
/>
|
||||
<button onClick={onSubmit}>GO</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
examples/get-started/hook/map.js
Normal file
22
examples/get-started/hook/map.js
Normal file
@ -0,0 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import Map from 'react-map-gl';
|
||||
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
const MAPBOX_TOKEN = ''; // Set your mapbox token here
|
||||
|
||||
export default function MapView() {
|
||||
return (
|
||||
<Map
|
||||
id="mymap"
|
||||
initialViewState={{
|
||||
longitude: -122.4,
|
||||
latitude: 37.8,
|
||||
zoom: 14
|
||||
}}
|
||||
style={{width: 800, height: 600}}
|
||||
mapStyle="mapbox://styles/mapbox/streets-v9"
|
||||
mapboxAccessToken={MAPBOX_TOKEN}
|
||||
/>
|
||||
);
|
||||
}
|
||||
24
examples/get-started/hook/package.json
Normal file
24
examples/get-started/hook/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --progress --hot --open",
|
||||
"build": "webpack --mode=production -o ./dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react-map-gl": "^7.0.0",
|
||||
"mapbox-gl": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"css-loader": "^6.5.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"style-loader": "^3.3.0",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.0",
|
||||
"webpack-dev-server": "^4.7.0"
|
||||
}
|
||||
}
|
||||
39
examples/get-started/hook/webpack.config.js
Normal file
39
examples/get-started/hook/webpack.config.js
Normal file
@ -0,0 +1,39 @@
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
|
||||
entry: {
|
||||
app: './app.js'
|
||||
},
|
||||
|
||||
devtool: 'source-map',
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: [/node_modules/],
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/env', '@babel/react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Optional: Enables reading mapbox token from environment variable
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({title: 'react-map-gl Example'}),
|
||||
new webpack.EnvironmentPlugin({MapboxAccessToken: ''})
|
||||
]
|
||||
};
|
||||
@ -28,7 +28,7 @@ function NumericInput({name, value, onChange}) {
|
||||
}
|
||||
|
||||
function ControlPanel(props) {
|
||||
const {settings, interactionState, onChange} = props;
|
||||
const {settings, onChange} = props;
|
||||
|
||||
const renderSetting = (name, value) => {
|
||||
switch (typeof value) {
|
||||
|
||||
@ -19,10 +19,7 @@ export default function App() {
|
||||
const mapRef = useRef<MapRef>();
|
||||
|
||||
const onSelectCity = useCallback(({longitude, latitude}) => {
|
||||
if (mapRef.current) {
|
||||
const map = mapRef.current.getMap();
|
||||
map.flyTo({center: [longitude, latitude]});
|
||||
}
|
||||
mapRef.current?.flyTo({center: [longitude, latitude], duration: 2000});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@ -17,12 +17,10 @@ export default function App() {
|
||||
const onClick = (event: MapLayerMouseEvent) => {
|
||||
const feature = event.features[0];
|
||||
if (feature) {
|
||||
const map = mapRef.current.getMap();
|
||||
|
||||
// calculate the bounding box of the feature
|
||||
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
|
||||
|
||||
map.fitBounds(
|
||||
mapRef.current.fitBounds(
|
||||
[
|
||||
[minLng, minLat],
|
||||
[maxLng, maxLat]
|
||||
|
||||
@ -17,7 +17,7 @@ export type AttributionControlProps = {
|
||||
};
|
||||
|
||||
function AttributionControl(props: AttributionControlProps): null {
|
||||
const ctrl = useControl(() => new mapboxgl.AttributionControl(props), props.position);
|
||||
useControl(() => new mapboxgl.AttributionControl(props), {position: props.position});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -14,12 +14,12 @@ export type FullscreenControlProps = {
|
||||
};
|
||||
|
||||
function FullscreenControl(props: FullscreenControlProps): null {
|
||||
const ctrl = useControl(
|
||||
useControl(
|
||||
() =>
|
||||
new mapboxgl.FullscreenControl({
|
||||
container: props.containerId && document.getElementById(props.containerId)
|
||||
}),
|
||||
props.position
|
||||
{position: props.position}
|
||||
);
|
||||
|
||||
return null;
|
||||
|
||||
@ -58,27 +58,30 @@ export type GeolocateControlProps = {
|
||||
const GeolocateControl = forwardRef<GeolocateControlRef, GeolocateControlProps>((props, ref) => {
|
||||
const thisRef = useRef({props});
|
||||
|
||||
const ctrl = useControl(() => {
|
||||
const gc = new mapboxgl.GeolocateControl(props);
|
||||
const ctrl = useControl(
|
||||
() => {
|
||||
const gc = new mapboxgl.GeolocateControl(props);
|
||||
|
||||
gc.on('geolocate', e => {
|
||||
thisRef.current.props.onGeolocate?.(e as GeolocateEvent);
|
||||
});
|
||||
gc.on('error', e => {
|
||||
thisRef.current.props.onError?.(e as GeolocateErrorEvent);
|
||||
});
|
||||
gc.on('outofmaxbounds', e => {
|
||||
thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateEvent);
|
||||
});
|
||||
gc.on('trackuserlocationstart', e => {
|
||||
thisRef.current.props.onTrackUserLocationStart?.(e as MapboxEvent);
|
||||
});
|
||||
gc.on('trackuserlocationend', e => {
|
||||
thisRef.current.props.onTrackUserLocationEnd?.(e as MapboxEvent);
|
||||
});
|
||||
gc.on('geolocate', e => {
|
||||
thisRef.current.props.onGeolocate?.(e as GeolocateEvent);
|
||||
});
|
||||
gc.on('error', e => {
|
||||
thisRef.current.props.onError?.(e as GeolocateErrorEvent);
|
||||
});
|
||||
gc.on('outofmaxbounds', e => {
|
||||
thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateEvent);
|
||||
});
|
||||
gc.on('trackuserlocationstart', e => {
|
||||
thisRef.current.props.onTrackUserLocationStart?.(e as MapboxEvent);
|
||||
});
|
||||
gc.on('trackuserlocationend', e => {
|
||||
thisRef.current.props.onTrackUserLocationEnd?.(e as MapboxEvent);
|
||||
});
|
||||
|
||||
return gc;
|
||||
}, props.position) as mapboxgl.GeolocateControl;
|
||||
return gc;
|
||||
},
|
||||
{position: props.position}
|
||||
) as mapboxgl.GeolocateControl;
|
||||
|
||||
thisRef.current.props = props;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {useContext, useEffect, useMemo, useState, useRef} from 'react';
|
||||
import MapContext from './map-context';
|
||||
import {MapContext} from './map';
|
||||
import assert from '../utils/assert';
|
||||
import {deepEqual} from '../utils/deep-equal';
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type {MapboxMap} from '../utils/types';
|
||||
|
||||
export default React.createContext<MapboxMap>(null);
|
||||
@ -1,17 +1,15 @@
|
||||
import * as React from 'react';
|
||||
import {useState, useRef, useEffect, forwardRef, useImperativeHandle} from 'react';
|
||||
import {useState, useRef, useEffect, useContext, forwardRef, useImperativeHandle} from 'react';
|
||||
|
||||
import Mapbox from '../mapbox/mapbox';
|
||||
import type {MapboxProps} from '../mapbox/mapbox';
|
||||
import MapContext from './map-context';
|
||||
import {MountedMapsContext} from './use-map';
|
||||
import Mapbox, {MapboxProps} from '../mapbox/mapbox';
|
||||
import createRef, {MapRef} from '../mapbox/create-ref';
|
||||
|
||||
import type {CSSProperties} from 'react';
|
||||
import type {MapboxMap} from '../utils/types';
|
||||
import useIsomorphicLayoutEffect from '../utils/use-isomorphic-layout-effect';
|
||||
|
||||
export interface MapRef {
|
||||
getMap(): MapboxMap;
|
||||
}
|
||||
export const MapContext = React.createContext<MapboxMap>(null);
|
||||
|
||||
export type MapProps = MapboxProps & {
|
||||
/** Map container id */
|
||||
@ -47,7 +45,8 @@ const defaultProps: MapProps = {
|
||||
renderWorldCopies: true
|
||||
};
|
||||
|
||||
const Map = forwardRef((props: MapProps, ref) => {
|
||||
const Map = forwardRef<MapRef, MapProps>((props, ref) => {
|
||||
const mountedMapsContext = useContext(MountedMapsContext);
|
||||
const [mapInstance, setMapInstance] = useState<Mapbox>(null);
|
||||
const containerRef = useRef();
|
||||
|
||||
@ -55,7 +54,12 @@ const Map = forwardRef((props: MapProps, ref) => {
|
||||
const map = new Mapbox(props);
|
||||
map.initialize(containerRef.current);
|
||||
setMapInstance(map);
|
||||
return () => map.destroy();
|
||||
mountedMapsContext?.onMapMount(createRef(map), props.id);
|
||||
|
||||
return () => {
|
||||
mountedMapsContext?.onMapUnmount(props.id);
|
||||
map.destroy();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
@ -64,13 +68,7 @@ const Map = forwardRef((props: MapProps, ref) => {
|
||||
}
|
||||
});
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
getMap: () => mapInstance.getMap()
|
||||
}),
|
||||
[mapInstance]
|
||||
);
|
||||
useImperativeHandle(ref, () => createRef(mapInstance), [mapInstance]);
|
||||
|
||||
const style: CSSProperties = {
|
||||
position: 'relative',
|
||||
@ -82,7 +80,7 @@ const Map = forwardRef((props: MapProps, ref) => {
|
||||
return (
|
||||
<div id={props.id} ref={containerRef} style={style}>
|
||||
{mapInstance && (
|
||||
<MapContext.Provider value={mapInstance.getMap()}>{props.children}</MapContext.Provider>
|
||||
<MapContext.Provider value={mapInstance.map}>{props.children}</MapContext.Provider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -6,7 +6,7 @@ import {useEffect, useState, useRef, useContext} from 'react';
|
||||
import mapboxgl from '../utils/mapboxgl';
|
||||
import type {MarkerDragEvent, MarkerOptions, MapboxPopup} from '../utils/types';
|
||||
|
||||
import MapContext from './map-context';
|
||||
import {MapContext} from './map';
|
||||
import {arePointsEqual} from '../utils/deep-equal';
|
||||
|
||||
export type MarkerProps = Omit<MarkerOptions, 'element'> & {
|
||||
|
||||
@ -16,7 +16,7 @@ export type NavigationControlProps = {
|
||||
};
|
||||
|
||||
function NavigationControl(props: NavigationControlProps): null {
|
||||
const ctrl = useControl(() => new mapboxgl.NavigationControl(props), props.position);
|
||||
useControl(() => new mapboxgl.NavigationControl(props), {position: props.position});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import {useEffect, useState, useRef, useContext} from 'react';
|
||||
import mapboxgl from '../utils/mapboxgl';
|
||||
import type {PopupOptions, MapboxEvent} from '../utils/types';
|
||||
|
||||
import MapContext from './map-context';
|
||||
import {MapContext} from './map';
|
||||
import {deepEqual} from '../utils/deep-equal';
|
||||
|
||||
export type PopupProps = PopupOptions & {
|
||||
|
||||
@ -19,10 +19,9 @@ const defaultProps: ScaleControlProps = {
|
||||
};
|
||||
|
||||
function ScaleControl(props: ScaleControlProps): null {
|
||||
const ctrl = useControl(
|
||||
() => new mapboxgl.ScaleControl(props),
|
||||
props.position
|
||||
) as mapboxgl.ScaleControl;
|
||||
const ctrl = useControl(() => new mapboxgl.ScaleControl(props), {
|
||||
position: props.position
|
||||
}) as mapboxgl.ScaleControl;
|
||||
|
||||
// @ts-ignore
|
||||
if (ctrl.options.unit !== props.unit || ctrl.options.maxWidth !== props.maxWidth) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import {useContext, useEffect, useMemo, useState, useRef} from 'react';
|
||||
import {cloneElement} from 'react';
|
||||
import MapContext from './map-context';
|
||||
import {MapContext} from './map';
|
||||
import assert from '../utils/assert';
|
||||
import {deepEqual} from '../utils/deep-equal';
|
||||
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
import {useContext, useState, useEffect} from 'react';
|
||||
import type {IControl, ControlPosition} from '../utils/types';
|
||||
import MapContext from './map-context';
|
||||
import type {IControl, ControlPosition, MapboxMap} from '../utils/types';
|
||||
import {MapContext} from './map';
|
||||
|
||||
export default function useControl(onCreate: () => IControl, position?: ControlPosition) {
|
||||
export default function useControl(
|
||||
onCreate: () => IControl,
|
||||
opts?: {
|
||||
position?: ControlPosition;
|
||||
onAdd?: (map: MapboxMap) => void;
|
||||
onRemove?: (map: MapboxMap) => void;
|
||||
}
|
||||
) {
|
||||
const map = useContext(MapContext);
|
||||
const [ctrl] = useState(onCreate);
|
||||
const [ctrl] = useState(onCreate());
|
||||
|
||||
useEffect(() => {
|
||||
map.addControl(ctrl, position);
|
||||
if (map) {
|
||||
map.addControl(ctrl, opts?.position);
|
||||
opts?.onAdd?.(map);
|
||||
|
||||
return () => {
|
||||
map.removeControl(ctrl);
|
||||
};
|
||||
}, []);
|
||||
return () => {
|
||||
opts?.onRemove?.(map);
|
||||
map.removeControl(ctrl);
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}, [map]);
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
52
src/components/use-map.tsx
Normal file
52
src/components/use-map.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import * as React from 'react';
|
||||
import {useState, useCallback, useContext} from 'react';
|
||||
|
||||
import {MapRef} from '../mapbox/create-ref';
|
||||
|
||||
type MountedMapsContextValue = {
|
||||
maps: {[id: string]: MapRef};
|
||||
onMapMount: (map: MapRef, id: string) => void;
|
||||
onMapUnmount: (id: string) => void;
|
||||
};
|
||||
|
||||
export const MountedMapsContext = React.createContext<MountedMapsContextValue>(null);
|
||||
|
||||
export const MapProvider: React.FC<{}> = props => {
|
||||
const [maps, setMaps] = useState<{[id: string]: MapRef}>({});
|
||||
|
||||
const onMapMount = useCallback(
|
||||
(map: MapRef, id: string = 'default') => {
|
||||
if (maps[id]) {
|
||||
throw new Error(`Multiple maps with the same id: ${id}`);
|
||||
}
|
||||
setMaps({...maps, [id]: map});
|
||||
},
|
||||
[maps]
|
||||
);
|
||||
|
||||
const onMapUnmount = useCallback(
|
||||
(id: string = 'default') => {
|
||||
const nextMaps = {...maps};
|
||||
delete nextMaps[id];
|
||||
setMaps(nextMaps);
|
||||
},
|
||||
[maps]
|
||||
);
|
||||
|
||||
return (
|
||||
<MountedMapsContext.Provider
|
||||
value={{
|
||||
maps,
|
||||
onMapMount,
|
||||
onMapUnmount
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</MountedMapsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export function useMap() {
|
||||
const {maps} = useContext(MountedMapsContext);
|
||||
return maps;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
export {default} from './components/map';
|
||||
|
||||
export {default as Map, MapProps, MapRef} from './components/map';
|
||||
export {default as Map, MapProps} from './components/map';
|
||||
export {MapRef} from './mapbox/create-ref';
|
||||
|
||||
export {default as Marker, MarkerProps} from './components/marker';
|
||||
export {default as Popup, PopupProps} from './components/popup';
|
||||
@ -22,5 +23,8 @@ export {default as ScaleControl, ScaleControlProps} from './components/scale-con
|
||||
export {default as Source, SourceProps} from './components/source';
|
||||
export {default as Layer, LayerProps} from './components/layer';
|
||||
|
||||
export {default as useControl} from './components/use-control';
|
||||
export {MapProvider, useMap} from './components/use-map';
|
||||
|
||||
// Types
|
||||
export * from './utils/types';
|
||||
|
||||
130
src/mapbox/create-ref.ts
Normal file
130
src/mapbox/create-ref.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import type {MapboxMap, ViewState} from '../utils/types';
|
||||
import type Mapbox from './mapbox';
|
||||
|
||||
/** mapboxgl.Map methods to forward to the ref object
|
||||
Object.getOwnPropertyNames(Object.getPrototypeOf(map))
|
||||
.filter(key => typeof temp1[key] === 'function' && key[0] != '_')
|
||||
*/
|
||||
const forwardMethods = [
|
||||
// 'getCenter',
|
||||
// 'setCenter',
|
||||
'panBy',
|
||||
'panTo',
|
||||
// 'getZoom',
|
||||
// 'setZoom',
|
||||
'zoomTo',
|
||||
'zoomIn',
|
||||
'zoomOut',
|
||||
// 'getBearing',
|
||||
// 'setBearing',
|
||||
// 'getPadding',
|
||||
// 'setPadding',
|
||||
'rotateTo',
|
||||
'resetNorth',
|
||||
'resetNorthPitch',
|
||||
'snapToNorth',
|
||||
// 'getPitch',
|
||||
// 'setPitch',
|
||||
'cameraForBounds',
|
||||
'fitBounds',
|
||||
'fitScreenCoordinates',
|
||||
'jumpTo',
|
||||
'getFreeCameraOptions',
|
||||
'setFreeCameraOptions',
|
||||
'easeTo',
|
||||
'flyTo',
|
||||
'isEasing',
|
||||
'stop',
|
||||
// "addControl",
|
||||
// "removeControl",
|
||||
// "hasControl",
|
||||
'getContainer',
|
||||
'getCanvasContainer',
|
||||
'getCanvas',
|
||||
'resize',
|
||||
'getBounds',
|
||||
// "getMaxBounds",
|
||||
// "setMaxBounds",
|
||||
// "setMinZoom",
|
||||
// "getMinZoom",
|
||||
// "setMaxZoom",
|
||||
// "getMaxZoom",
|
||||
// "setMinPitch",
|
||||
// "getMinPitch",
|
||||
// "setMaxPitch",
|
||||
// "getMaxPitch",
|
||||
// "getRenderWorldCopies",
|
||||
// "setRenderWorldCopies",
|
||||
// "getProjection",
|
||||
// "setProjection",
|
||||
'project',
|
||||
'unproject',
|
||||
'isMoving',
|
||||
'isZooming',
|
||||
'isRotating',
|
||||
'on',
|
||||
'once',
|
||||
'off',
|
||||
'queryRenderedFeatures',
|
||||
'querySourceFeatures',
|
||||
'queryTerrainElevation',
|
||||
// "setStyle",
|
||||
// "getStyle",
|
||||
'isStyleLoaded',
|
||||
// "addSource",
|
||||
'isSourceLoaded',
|
||||
'areTilesLoaded',
|
||||
// "addSourceType",
|
||||
// "removeSource",
|
||||
'getSource',
|
||||
'addImage',
|
||||
'updateImage',
|
||||
'hasImage',
|
||||
'removeImage',
|
||||
'loadImage',
|
||||
'listImages',
|
||||
// "addLayer",
|
||||
'moveLayer',
|
||||
// "removeLayer",
|
||||
'getLayer',
|
||||
// "setLayerZoomRange",
|
||||
// "setFilter",
|
||||
// "getFilter",
|
||||
// "setPaintProperty",
|
||||
// "getPaintProperty",
|
||||
// "setLayoutProperty",
|
||||
// "getLayoutProperty",
|
||||
'setLight',
|
||||
'getLight',
|
||||
'setTerrain',
|
||||
'getTerrain',
|
||||
'setFog',
|
||||
'getFog',
|
||||
'setFeatureState',
|
||||
'removeFeatureState',
|
||||
'getFeatureState',
|
||||
'loaded'
|
||||
// "remove",
|
||||
// "triggerRepaint"
|
||||
] as const;
|
||||
|
||||
export type MapRef = {
|
||||
getMap(): MapboxMap;
|
||||
getViewState(): ViewState;
|
||||
} & Pick<MapboxMap, typeof forwardMethods[number]>;
|
||||
|
||||
export default function createRef(mapInstance: Mapbox): MapRef {
|
||||
if (!mapInstance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result: any = {
|
||||
getMap: () => mapInstance.map,
|
||||
getViewState: () => mapInstance.viewState
|
||||
};
|
||||
for (const key of forwardMethods) {
|
||||
result[key] = mapInstance.map[key].bind(mapInstance.map);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -19,7 +19,8 @@ import type {
|
||||
MapDataEvent,
|
||||
MapboxEvent,
|
||||
ErrorEvent,
|
||||
MapboxGeoJSONFeature
|
||||
MapboxGeoJSONFeature,
|
||||
MapboxMap
|
||||
} from '../utils/types';
|
||||
|
||||
export type MapboxProps = Omit<
|
||||
@ -198,8 +199,12 @@ export default class Mapbox {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
getMap() {
|
||||
return this._map;
|
||||
get map(): MapboxMap {
|
||||
return this._map as MapboxMap;
|
||||
}
|
||||
|
||||
get viewState(): ViewState {
|
||||
return transformToViewState(this._renderTransform);
|
||||
}
|
||||
|
||||
setProps(props: MapboxProps) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user