react-bkoi-gl 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/bkoi-gl/package.json +6 -0
- package/dist/components/attribution-control.d.ts +11 -0
- package/dist/components/attribution-control.js +19 -0
- package/dist/components/attribution-control.js.map +1 -0
- package/dist/components/fullscreen-control.d.ts +14 -0
- package/dist/components/fullscreen-control.js +21 -0
- package/dist/components/fullscreen-control.js.map +1 -0
- package/dist/components/geolocate-control.d.ts +37 -0
- package/dist/components/geolocate-control.js +51 -0
- package/dist/components/geolocate-control.js.map +1 -0
- package/dist/components/layer.d.ts +17 -0
- package/dist/components/layer.js +99 -0
- package/dist/components/layer.js.map +1 -0
- package/dist/components/map.d.ts +23 -0
- package/dist/components/map.js +102 -0
- package/dist/components/map.js.map +1 -0
- package/dist/components/marker.d.ts +41 -0
- package/dist/components/marker.js +102 -0
- package/dist/components/marker.js.map +1 -0
- package/dist/components/navigation-control.d.ts +11 -0
- package/dist/components/navigation-control.js +19 -0
- package/dist/components/navigation-control.js.map +1 -0
- package/dist/components/popup.d.ts +33 -0
- package/dist/components/popup.js +81 -0
- package/dist/components/popup.js.map +1 -0
- package/dist/components/scale-control.d.ts +13 -0
- package/dist/components/scale-control.js +31 -0
- package/dist/components/scale-control.js.map +1 -0
- package/dist/components/source.d.ts +7 -0
- package/dist/components/source.js +98 -0
- package/dist/components/source.js.map +1 -0
- package/dist/components/use-control.d.ts +9 -0
- package/dist/components/use-control.js +31 -0
- package/dist/components/use-control.js.map +1 -0
- package/dist/components/use-map.d.ts +20 -0
- package/dist/components/use-map.js +55 -0
- package/dist/components/use-map.js.map +1 -0
- package/dist/exports-bkoi-gl.cjs +1512 -0
- package/dist/exports-bkoi-gl.d.ts +89 -0
- package/dist/exports-bkoi-gl.js +36 -0
- package/dist/exports-bkoi-gl.js.map +1 -0
- package/dist/exports-mapbox.d.ts +139 -0
- package/dist/exports-mapbox.js +36 -0
- package/dist/exports-mapbox.js.map +1 -0
- package/dist/index.cjs +1514 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/mapbox/create-ref.d.ts +9 -0
- package/dist/mapbox/create-ref.js +64 -0
- package/dist/mapbox/create-ref.js.map +1 -0
- package/dist/mapbox/mapbox.d.ts +81 -0
- package/dist/mapbox/mapbox.js +477 -0
- package/dist/mapbox/mapbox.js.map +1 -0
- package/dist/styles/react-bkoi-gl.css +1 -0
- package/dist/types/common.d.ts +78 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/events-bkoi-gl.d.ts +49 -0
- package/dist/types/events-bkoi-gl.js +2 -0
- package/dist/types/events-bkoi-gl.js.map +1 -0
- package/dist/types/events-mapbox.d.ts +49 -0
- package/dist/types/events-mapbox.js +2 -0
- package/dist/types/events-mapbox.js.map +1 -0
- package/dist/types/events.d.ts +39 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +70 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lib.d.ts +224 -0
- package/dist/types/lib.js +2 -0
- package/dist/types/lib.js.map +1 -0
- package/dist/types/public.d.ts +2 -0
- package/dist/types/public.js +2 -0
- package/dist/types/public.js.map +1 -0
- package/dist/types/style-spec-bkoi-gl.d.ts +9 -0
- package/dist/types/style-spec-bkoi-gl.js +2 -0
- package/dist/types/style-spec-bkoi-gl.js.map +1 -0
- package/dist/types/style-spec-mapbox.d.ts +7 -0
- package/dist/types/style-spec-mapbox.js +2 -0
- package/dist/types/style-spec-mapbox.js.map +1 -0
- package/dist/utils/apply-react-style.d.ts +2 -0
- package/dist/utils/apply-react-style.js +16 -0
- package/dist/utils/apply-react-style.js.map +1 -0
- package/dist/utils/assert.d.ts +1 -0
- package/dist/utils/assert.js +6 -0
- package/dist/utils/assert.js.map +1 -0
- package/dist/utils/deep-equal.d.ts +15 -0
- package/dist/utils/deep-equal.js +46 -0
- package/dist/utils/deep-equal.js.map +1 -0
- package/dist/utils/set-globals.d.ts +22 -0
- package/dist/utils/set-globals.js +19 -0
- package/dist/utils/set-globals.js.map +1 -0
- package/dist/utils/style-utils.d.ts +2 -0
- package/dist/utils/style-utils.js +42 -0
- package/dist/utils/style-utils.js.map +1 -0
- package/dist/utils/transform.d.ts +26 -0
- package/dist/utils/transform.js +56 -0
- package/dist/utils/transform.js.map +1 -0
- package/dist/utils/use-isomorphic-layout-effect.d.ts +3 -0
- package/dist/utils/use-isomorphic-layout-effect.js +4 -0
- package/dist/utils/use-isomorphic-layout-effect.js.map +1 -0
- package/package.json +78 -0
- package/src/components/attribution-control.ts +32 -0
- package/src/components/fullscreen-control.tsx +37 -0
- package/src/components/geolocate-control.ts +90 -0
- package/src/components/layer.ts +133 -0
- package/src/components/map.tsx +155 -0
- package/src/components/marker.ts +137 -0
- package/src/components/navigation-control.ts +29 -0
- package/src/components/popup.ts +116 -0
- package/src/components/scale-control.ts +46 -0
- package/src/components/source.ts +158 -0
- package/src/components/use-control.ts +64 -0
- package/src/components/use-map.tsx +69 -0
- package/src/exports-bkoi-gl.ts +156 -0
- package/src/exports-mapbox.ts +159 -0
- package/src/index.ts +2 -0
- package/src/mapbox/create-ref.ts +118 -0
- package/src/mapbox/mapbox.ts +742 -0
- package/src/types/common.ts +94 -0
- package/src/types/events-bkoi-gl.ts +78 -0
- package/src/types/events-mapbox.ts +76 -0
- package/src/types/events.ts +73 -0
- package/src/types/index.ts +93 -0
- package/src/types/lib.ts +279 -0
- package/src/types/public.ts +9 -0
- package/src/types/style-spec-bkoi-gl.ts +81 -0
- package/src/types/style-spec-mapbox.ts +80 -0
- package/src/utils/apply-react-style.ts +20 -0
- package/src/utils/assert.ts +5 -0
- package/src/utils/deep-equal.ts +61 -0
- package/src/utils/set-globals.ts +57 -0
- package/src/utils/style-utils.ts +59 -0
- package/src/utils/transform.ts +86 -0
- package/src/utils/use-isomorphic-layout-effect.ts +7 -0
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-bkoi-gl",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "React components for Barikoi GL JS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"author": "barikoi",
|
|
10
|
+
"readme": "README.md",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://docs.barikoi.com/docs/maps-api",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"require": "./dist/index.cjs",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./bkoi-gl": {
|
|
20
|
+
"types": "./dist/exports-bkoi-gl.d.ts",
|
|
21
|
+
"require": "./dist/exports-bkoi-gl.cjs",
|
|
22
|
+
"import": "./dist/exports-bkoi-gl.js"
|
|
23
|
+
},
|
|
24
|
+
"./styles": {
|
|
25
|
+
"import": "./dist/styles/react-bkoi-gl.css",
|
|
26
|
+
"default": "./dist/styles/react-bkoi-gl.css"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"src",
|
|
31
|
+
"dist",
|
|
32
|
+
"bkoi-gl",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
37
|
+
"build": "ocular-clean && ocular-build && node scripts/copy-css.js",
|
|
38
|
+
"lint": "ocular-lint",
|
|
39
|
+
"cover": "ocular-test cover",
|
|
40
|
+
"test": "yarn typecheck && ocular-test",
|
|
41
|
+
"test-fast": "yarn typecheck && ocular-test node",
|
|
42
|
+
"metrics": "ocular-metrics",
|
|
43
|
+
"prepare": "husky"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@maplibre/maplibre-gl-style-spec": "^19.2.1",
|
|
47
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
48
|
+
"bkoi-gl": ">=2.0.0",
|
|
49
|
+
"mapbox-gl": "^1.13.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": ">=16.3.0",
|
|
53
|
+
"react-dom": ">=16.3.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@commitlint/cli": "^19.4.1",
|
|
57
|
+
"@commitlint/config-conventional": "^19.4.1",
|
|
58
|
+
"bkoi-gl": ">=2.0.0",
|
|
59
|
+
"@types/mapbox-gl": "^3.4.0",
|
|
60
|
+
"@types/react": "^16.0.0",
|
|
61
|
+
"husky": "^9.1.5",
|
|
62
|
+
"ocular-dev-tools": "2.0.0-alpha.15",
|
|
63
|
+
"react": "^18.0.0",
|
|
64
|
+
"react-dom": "^18.0.0",
|
|
65
|
+
"typescript": "^4.0.0"
|
|
66
|
+
},
|
|
67
|
+
"keywords": [
|
|
68
|
+
"barikoi",
|
|
69
|
+
"bkoi-gl",
|
|
70
|
+
"react",
|
|
71
|
+
"react-bkoi-gl",
|
|
72
|
+
"react bkoi"
|
|
73
|
+
],
|
|
74
|
+
"repository": {
|
|
75
|
+
"type": "git",
|
|
76
|
+
"url": "https://github.com/barikoi/react-bkoi-gl.git"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {useEffect, memo} from 'react';
|
|
3
|
+
import {applyReactStyle} from '../utils/apply-react-style';
|
|
4
|
+
import useControl from './use-control';
|
|
5
|
+
|
|
6
|
+
import type {ControlPosition, AttributionControlInstance} from '../types';
|
|
7
|
+
|
|
8
|
+
export type AttributionControlProps<OptionsT> = OptionsT & {
|
|
9
|
+
/** Placement of the control relative to the map. */
|
|
10
|
+
position?: ControlPosition;
|
|
11
|
+
/** CSS style override, applied to the control's container */
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function AttributionControl<AttributionControlOptions, ControlT extends AttributionControlInstance>(
|
|
16
|
+
props: AttributionControlProps<AttributionControlOptions>
|
|
17
|
+
): null {
|
|
18
|
+
const ctrl = useControl<ControlT>(
|
|
19
|
+
({mapLib}) => new mapLib.AttributionControl(props) as ControlT,
|
|
20
|
+
{
|
|
21
|
+
position: props.position
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
applyReactStyle(ctrl._container, props.style);
|
|
27
|
+
}, [props.style]);
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default memo(AttributionControl);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* global document */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import {useEffect, memo} from 'react';
|
|
4
|
+
import {applyReactStyle} from '../utils/apply-react-style';
|
|
5
|
+
import useControl from './use-control';
|
|
6
|
+
|
|
7
|
+
import type {ControlPosition, FullscreenControlInstance} from '../types';
|
|
8
|
+
|
|
9
|
+
export type FullscreenControlProps<OptionsT> = Omit<OptionsT, 'container'> & {
|
|
10
|
+
/** Id of the DOM element which should be made full screen. By default, the map container
|
|
11
|
+
* element will be made full screen. */
|
|
12
|
+
containerId?: string;
|
|
13
|
+
/** Placement of the control relative to the map. */
|
|
14
|
+
position?: ControlPosition;
|
|
15
|
+
/** CSS style override, applied to the control's container */
|
|
16
|
+
style?: React.CSSProperties;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function FullscreenControl<FullscreenControlOptions, ControlT extends FullscreenControlInstance>(
|
|
20
|
+
props: FullscreenControlProps<FullscreenControlOptions>
|
|
21
|
+
): null {
|
|
22
|
+
const ctrl = useControl<ControlT>(
|
|
23
|
+
({mapLib}) =>
|
|
24
|
+
new mapLib.FullscreenControl({
|
|
25
|
+
container: props.containerId && document.getElementById(props.containerId)
|
|
26
|
+
}) as ControlT,
|
|
27
|
+
{position: props.position}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
applyReactStyle(ctrl._controlContainer, props.style);
|
|
32
|
+
}, [props.style]);
|
|
33
|
+
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default memo(FullscreenControl);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {useImperativeHandle, useRef, useEffect, forwardRef, memo} from 'react';
|
|
3
|
+
import {applyReactStyle} from '../utils/apply-react-style';
|
|
4
|
+
import useControl from './use-control';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ControlPosition,
|
|
8
|
+
GeolocateControlInstance,
|
|
9
|
+
GeolocateEvent,
|
|
10
|
+
GeolocateResultEvent,
|
|
11
|
+
GeolocateErrorEvent
|
|
12
|
+
} from '../types';
|
|
13
|
+
|
|
14
|
+
export type GeolocateControlProps<
|
|
15
|
+
OptionsT,
|
|
16
|
+
ControlT extends GeolocateControlInstance
|
|
17
|
+
> = OptionsT & {
|
|
18
|
+
/** Placement of the control relative to the map. */
|
|
19
|
+
position?: ControlPosition;
|
|
20
|
+
/** CSS style override, applied to the control's container */
|
|
21
|
+
style?: React.CSSProperties;
|
|
22
|
+
|
|
23
|
+
/** Called on each Geolocation API position update that returned as success. */
|
|
24
|
+
onGeolocate?: (e: GeolocateResultEvent<ControlT>) => void;
|
|
25
|
+
/** Called on each Geolocation API position update that returned as an error. */
|
|
26
|
+
onError?: (e: GeolocateErrorEvent<ControlT>) => void;
|
|
27
|
+
/** Called on each Geolocation API position update that returned as success but user position
|
|
28
|
+
* is out of map `maxBounds`. */
|
|
29
|
+
onOutOfMaxBounds?: (e: GeolocateResultEvent<ControlT>) => void;
|
|
30
|
+
/** Called when the GeolocateControl changes to the active lock state. */
|
|
31
|
+
onTrackUserLocationStart?: (e: GeolocateEvent<ControlT>) => void;
|
|
32
|
+
/** Called when the GeolocateControl changes to the background state. */
|
|
33
|
+
onTrackUserLocationEnd?: (e: GeolocateEvent<ControlT>) => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function GeolocateControl<GeolocateControlOptions, ControlT extends GeolocateControlInstance>(
|
|
37
|
+
props: GeolocateControlProps<GeolocateControlOptions, ControlT>,
|
|
38
|
+
ref: React.Ref<ControlT>
|
|
39
|
+
) {
|
|
40
|
+
const thisRef = useRef({props});
|
|
41
|
+
|
|
42
|
+
const ctrl = useControl<ControlT>(
|
|
43
|
+
({mapLib}) => {
|
|
44
|
+
const gc = new mapLib.GeolocateControl(props) as ControlT;
|
|
45
|
+
|
|
46
|
+
// Hack: fix GeolocateControl reuse
|
|
47
|
+
// When using React strict mode, the component is mounted twice.
|
|
48
|
+
// GeolocateControl's UI creation is asynchronous. Removing and adding it back causes the UI to be initialized twice.
|
|
49
|
+
// @ts-expect-error private method
|
|
50
|
+
const setupUI = gc._setupUI;
|
|
51
|
+
// @ts-expect-error private method
|
|
52
|
+
gc._setupUI = args => {
|
|
53
|
+
if (!gc._container.hasChildNodes()) {
|
|
54
|
+
setupUI(args);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
gc.on('geolocate', e => {
|
|
59
|
+
thisRef.current.props.onGeolocate?.(e as GeolocateResultEvent<ControlT>);
|
|
60
|
+
});
|
|
61
|
+
gc.on('error', e => {
|
|
62
|
+
thisRef.current.props.onError?.(e as GeolocateErrorEvent<ControlT>);
|
|
63
|
+
});
|
|
64
|
+
gc.on('outofmaxbounds', e => {
|
|
65
|
+
thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateResultEvent<ControlT>);
|
|
66
|
+
});
|
|
67
|
+
gc.on('trackuserlocationstart', e => {
|
|
68
|
+
thisRef.current.props.onTrackUserLocationStart?.(e as GeolocateEvent<ControlT>);
|
|
69
|
+
});
|
|
70
|
+
gc.on('trackuserlocationend', e => {
|
|
71
|
+
thisRef.current.props.onTrackUserLocationEnd?.(e as GeolocateEvent<ControlT>);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return gc;
|
|
75
|
+
},
|
|
76
|
+
{position: props.position}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
thisRef.current.props = props;
|
|
80
|
+
|
|
81
|
+
useImperativeHandle(ref, () => ctrl, []);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
applyReactStyle(ctrl._container, props.style);
|
|
85
|
+
}, [props.style]);
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default memo(forwardRef(GeolocateControl));
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {useContext, useEffect, useMemo, useState, useRef} from 'react';
|
|
2
|
+
import {MapContext} from './map';
|
|
3
|
+
import assert from '../utils/assert';
|
|
4
|
+
import {deepEqual} from '../utils/deep-equal';
|
|
5
|
+
|
|
6
|
+
import type {MapInstance, CustomLayerInterface, ILayer} from '../types';
|
|
7
|
+
|
|
8
|
+
// Omiting property from a union type, see
|
|
9
|
+
// https://github.com/microsoft/TypeScript/issues/39556#issuecomment-656925230
|
|
10
|
+
type OptionalId<T> = T extends {id: string} ? Omit<T, 'id'> & {id?: string} : T;
|
|
11
|
+
type OptionalSource<T> = T extends {source: string} ? Omit<T, 'source'> & {source?: string} : T;
|
|
12
|
+
|
|
13
|
+
export type LayerProps<LayerT> = OptionalSource<OptionalId<LayerT>> & {
|
|
14
|
+
/** If set, the layer will be inserted before the specified layer */
|
|
15
|
+
beforeId?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/* eslint-disable complexity, max-statements */
|
|
19
|
+
function updateLayer<LayerT extends ILayer>(
|
|
20
|
+
map: MapInstance,
|
|
21
|
+
id: string,
|
|
22
|
+
props: LayerProps<LayerT>,
|
|
23
|
+
prevProps: LayerProps<LayerT>
|
|
24
|
+
) {
|
|
25
|
+
assert(props.id === prevProps.id, 'layer id changed');
|
|
26
|
+
assert(props.type === prevProps.type, 'layer type changed');
|
|
27
|
+
|
|
28
|
+
if (props.type === 'custom' || prevProps.type === 'custom') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const {layout = {}, paint = {}, filter, minzoom, maxzoom, beforeId} = props;
|
|
33
|
+
|
|
34
|
+
if (beforeId !== prevProps.beforeId) {
|
|
35
|
+
map.moveLayer(id, beforeId);
|
|
36
|
+
}
|
|
37
|
+
if (layout !== prevProps.layout) {
|
|
38
|
+
const prevLayout = prevProps.layout || {};
|
|
39
|
+
for (const key in layout) {
|
|
40
|
+
if (!deepEqual(layout[key], prevLayout[key])) {
|
|
41
|
+
map.setLayoutProperty(id, key, layout[key]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const key in prevLayout) {
|
|
45
|
+
if (!layout.hasOwnProperty(key)) {
|
|
46
|
+
map.setLayoutProperty(id, key, undefined);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (paint !== prevProps.paint) {
|
|
51
|
+
const prevPaint = prevProps.paint || {};
|
|
52
|
+
for (const key in paint) {
|
|
53
|
+
if (!deepEqual(paint[key], prevPaint[key])) {
|
|
54
|
+
map.setPaintProperty(id, key, paint[key]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const key in prevPaint) {
|
|
58
|
+
if (!paint.hasOwnProperty(key)) {
|
|
59
|
+
map.setPaintProperty(id, key, undefined);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!deepEqual(filter, prevProps.filter)) {
|
|
65
|
+
map.setFilter(id, filter);
|
|
66
|
+
}
|
|
67
|
+
if (minzoom !== prevProps.minzoom || maxzoom !== prevProps.maxzoom) {
|
|
68
|
+
map.setLayerZoomRange(id, minzoom, maxzoom);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createLayer<LayerT extends ILayer>(
|
|
73
|
+
map: MapInstance,
|
|
74
|
+
id: string,
|
|
75
|
+
props: LayerProps<LayerT>
|
|
76
|
+
) {
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
if (map.style && map.style._loaded && (!('source' in props) || map.getSource(props.source))) {
|
|
79
|
+
const options: LayerProps<LayerT> = {...props, id};
|
|
80
|
+
delete options.beforeId;
|
|
81
|
+
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
map.addLayer(options, props.beforeId);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* eslint-enable complexity, max-statements */
|
|
88
|
+
|
|
89
|
+
let layerCounter = 0;
|
|
90
|
+
|
|
91
|
+
function Layer<LayerT extends ILayer>(props: LayerProps<LayerT | CustomLayerInterface>) {
|
|
92
|
+
const map = useContext(MapContext).map.getMap();
|
|
93
|
+
const propsRef = useRef(props);
|
|
94
|
+
const [, setStyleLoaded] = useState(0);
|
|
95
|
+
|
|
96
|
+
const id = useMemo(() => props.id || `jsx-layer-${layerCounter++}`, []);
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (map) {
|
|
100
|
+
const forceUpdate = () => setStyleLoaded(version => version + 1);
|
|
101
|
+
map.on('styledata', forceUpdate);
|
|
102
|
+
forceUpdate();
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
map.off('styledata', forceUpdate);
|
|
106
|
+
// @ts-ignore
|
|
107
|
+
if (map.style && map.style._loaded && map.getLayer(id)) {
|
|
108
|
+
map.removeLayer(id);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}, [map]);
|
|
114
|
+
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
const layer = map && map.style && map.getLayer(id);
|
|
117
|
+
if (layer) {
|
|
118
|
+
try {
|
|
119
|
+
updateLayer(map, id, props, propsRef.current);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.warn(error); // eslint-disable-line
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
createLayer(map, id, props);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Store last rendered props
|
|
128
|
+
propsRef.current = props;
|
|
129
|
+
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default Layer;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {useState, useRef, useEffect, useContext, useMemo, useImperativeHandle} from 'react';
|
|
3
|
+
|
|
4
|
+
import {MountedMapsContext} from './use-map';
|
|
5
|
+
import Mapbox, {MapboxProps} from '../mapbox/mapbox';
|
|
6
|
+
import createRef, {MapRef} from '../mapbox/create-ref';
|
|
7
|
+
|
|
8
|
+
import type {CSSProperties} from 'react';
|
|
9
|
+
import useIsomorphicLayoutEffect from '../utils/use-isomorphic-layout-effect';
|
|
10
|
+
import setGlobals, {GlobalSettings} from '../utils/set-globals';
|
|
11
|
+
import type {MapLib, MapInstance, MapStyle, Callbacks} from '../types';
|
|
12
|
+
|
|
13
|
+
export type MapContextValue<MapT extends MapInstance = MapInstance> = {
|
|
14
|
+
mapLib: MapLib<MapT>;
|
|
15
|
+
map: MapRef<MapT>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const MapContext = React.createContext<MapContextValue>(null);
|
|
19
|
+
|
|
20
|
+
type MapInitOptions<MapOptions> = Omit<
|
|
21
|
+
MapOptions,
|
|
22
|
+
'style' | 'container' | 'bounds' | 'fitBoundsOptions' | 'center'
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
export type MapProps<
|
|
26
|
+
MapOptions,
|
|
27
|
+
StyleT extends MapStyle,
|
|
28
|
+
CallbacksT extends Callbacks,
|
|
29
|
+
MapT extends MapInstance
|
|
30
|
+
> = MapInitOptions<MapOptions> &
|
|
31
|
+
MapboxProps<StyleT, CallbacksT> &
|
|
32
|
+
GlobalSettings & {
|
|
33
|
+
mapLib?: MapLib<MapT> | Promise<MapLib<MapT>>;
|
|
34
|
+
reuseMaps?: boolean;
|
|
35
|
+
/** Map container id */
|
|
36
|
+
id?: string;
|
|
37
|
+
/** Map container CSS style */
|
|
38
|
+
style?: CSSProperties;
|
|
39
|
+
children?: any;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default function Map<
|
|
43
|
+
MapOptions,
|
|
44
|
+
StyleT extends MapStyle,
|
|
45
|
+
CallbacksT extends Callbacks,
|
|
46
|
+
MapT extends MapInstance
|
|
47
|
+
>(
|
|
48
|
+
props: MapProps<MapOptions, StyleT, CallbacksT, MapT>,
|
|
49
|
+
ref: React.Ref<MapRef<MapT>>,
|
|
50
|
+
defaultLib: MapLib<MapT> | Promise<MapLib<MapT>>
|
|
51
|
+
) {
|
|
52
|
+
const mountedMapsContext = useContext(MountedMapsContext);
|
|
53
|
+
const [mapInstance, setMapInstance] = useState<Mapbox<StyleT, CallbacksT, MapT>>(null);
|
|
54
|
+
const containerRef = useRef();
|
|
55
|
+
|
|
56
|
+
const {current: contextValue} = useRef<MapContextValue<MapT>>({mapLib: null, map: null});
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const mapLib = props.mapLib;
|
|
60
|
+
let isMounted = true;
|
|
61
|
+
let mapbox: Mapbox<StyleT, CallbacksT, MapT>;
|
|
62
|
+
|
|
63
|
+
Promise.resolve(mapLib || defaultLib)
|
|
64
|
+
.then((module: MapLib<MapT> | {default: MapLib<MapT>}) => {
|
|
65
|
+
if (!isMounted) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!module) {
|
|
69
|
+
throw new Error('Invalid mapLib');
|
|
70
|
+
}
|
|
71
|
+
const mapboxgl = 'Map' in module ? module : module.default;
|
|
72
|
+
if (!mapboxgl.Map) {
|
|
73
|
+
throw new Error('Invalid mapLib');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// workerUrl & workerClass may change the result of supported()
|
|
77
|
+
// https://github.com/visgl/react-map-gl/discussions/2027
|
|
78
|
+
setGlobals(mapboxgl, props);
|
|
79
|
+
if (!mapboxgl.supported || mapboxgl.supported(props)) {
|
|
80
|
+
if (props.reuseMaps) {
|
|
81
|
+
mapbox = Mapbox.reuse(props, containerRef.current);
|
|
82
|
+
}
|
|
83
|
+
if (!mapbox) {
|
|
84
|
+
mapbox = new Mapbox(mapboxgl.Map, props, containerRef.current);
|
|
85
|
+
}
|
|
86
|
+
contextValue.map = createRef(mapbox);
|
|
87
|
+
contextValue.mapLib = mapboxgl;
|
|
88
|
+
|
|
89
|
+
setMapInstance(mapbox);
|
|
90
|
+
mountedMapsContext?.onMapMount(contextValue.map, props.id);
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error('Map is not supported by this browser');
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.catch(error => {
|
|
96
|
+
const {onError} = props;
|
|
97
|
+
if (onError) {
|
|
98
|
+
onError({
|
|
99
|
+
type: 'error',
|
|
100
|
+
target: null,
|
|
101
|
+
originalEvent: null,
|
|
102
|
+
error
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
console.error(error); // eslint-disable-line
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
isMounted = false;
|
|
111
|
+
if (mapbox) {
|
|
112
|
+
mountedMapsContext?.onMapUnmount(props.id);
|
|
113
|
+
if (props.reuseMaps) {
|
|
114
|
+
mapbox.recycle();
|
|
115
|
+
} else {
|
|
116
|
+
mapbox.destroy();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}, []);
|
|
121
|
+
|
|
122
|
+
useIsomorphicLayoutEffect(() => {
|
|
123
|
+
if (mapInstance) {
|
|
124
|
+
mapInstance.setProps(props);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
useImperativeHandle(ref, () => contextValue.map, [mapInstance]);
|
|
129
|
+
|
|
130
|
+
const style: CSSProperties = useMemo(
|
|
131
|
+
() => ({
|
|
132
|
+
position: 'relative',
|
|
133
|
+
width: '100%',
|
|
134
|
+
height: '100%',
|
|
135
|
+
...props.style
|
|
136
|
+
}),
|
|
137
|
+
[props.style]
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const CHILD_CONTAINER_STYLE = {
|
|
141
|
+
height: '100%'
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div id={props.id} ref={containerRef} style={style}>
|
|
146
|
+
{mapInstance && (
|
|
147
|
+
<MapContext.Provider value={contextValue}>
|
|
148
|
+
<div mapboxgl-children="" style={CHILD_CONTAINER_STYLE}>
|
|
149
|
+
{props.children}
|
|
150
|
+
</div>
|
|
151
|
+
</MapContext.Provider>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* global document */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import {createPortal} from 'react-dom';
|
|
4
|
+
import {useImperativeHandle, useEffect, useMemo, useRef, useContext, forwardRef, memo} from 'react';
|
|
5
|
+
import {applyReactStyle} from '../utils/apply-react-style';
|
|
6
|
+
|
|
7
|
+
import type {MarkerEvent, MarkerDragEvent, PointLike, MarkerInstance} from '../types';
|
|
8
|
+
|
|
9
|
+
import {MapContext} from './map';
|
|
10
|
+
import {arePointsEqual} from '../utils/deep-equal';
|
|
11
|
+
|
|
12
|
+
export type MarkerProps<OptionsT, MarkerT extends MarkerInstance> = OptionsT & {
|
|
13
|
+
/** Longitude of the anchor location */
|
|
14
|
+
longitude: number;
|
|
15
|
+
/** Latitude of the anchor location */
|
|
16
|
+
latitude: number;
|
|
17
|
+
|
|
18
|
+
// These types will be further constraint by OptionsT
|
|
19
|
+
draggable?: boolean;
|
|
20
|
+
offset?: PointLike;
|
|
21
|
+
pitchAlignment?: string;
|
|
22
|
+
rotation?: number;
|
|
23
|
+
rotationAlignment?: string;
|
|
24
|
+
popup?: any;
|
|
25
|
+
|
|
26
|
+
/** CSS style override, applied to the control's container */
|
|
27
|
+
style?: React.CSSProperties;
|
|
28
|
+
onClick?: (e: MarkerEvent<MarkerT, MouseEvent>) => void;
|
|
29
|
+
onDragStart?: (e: MarkerDragEvent<MarkerT>) => void;
|
|
30
|
+
onDrag?: (e: MarkerDragEvent<MarkerT>) => void;
|
|
31
|
+
onDragEnd?: (e: MarkerDragEvent<MarkerT>) => void;
|
|
32
|
+
children?: React.ReactNode;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/* eslint-disable complexity,max-statements */
|
|
36
|
+
function Marker<MarkerOptions, MarkerT extends MarkerInstance>(
|
|
37
|
+
props: MarkerProps<MarkerOptions, MarkerT>,
|
|
38
|
+
ref: React.Ref<MarkerT>
|
|
39
|
+
) {
|
|
40
|
+
const {map, mapLib} = useContext(MapContext);
|
|
41
|
+
const thisRef = useRef({props});
|
|
42
|
+
thisRef.current.props = props;
|
|
43
|
+
|
|
44
|
+
const marker: MarkerT = useMemo(() => {
|
|
45
|
+
let hasChildren = false;
|
|
46
|
+
React.Children.forEach(props.children, el => {
|
|
47
|
+
if (el) {
|
|
48
|
+
hasChildren = true;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const options = {
|
|
52
|
+
...props,
|
|
53
|
+
element: hasChildren ? document.createElement('div') : null
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const mk = new mapLib.Marker(options) as MarkerT;
|
|
57
|
+
mk.setLngLat([props.longitude, props.latitude]);
|
|
58
|
+
|
|
59
|
+
mk.getElement().addEventListener('click', (e: MouseEvent) => {
|
|
60
|
+
thisRef.current.props.onClick?.({
|
|
61
|
+
type: 'click',
|
|
62
|
+
target: mk,
|
|
63
|
+
originalEvent: e
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
mk.on('dragstart', e => {
|
|
68
|
+
const evt = e as MarkerDragEvent<MarkerT>;
|
|
69
|
+
evt.lngLat = marker.getLngLat();
|
|
70
|
+
thisRef.current.props.onDragStart?.(evt);
|
|
71
|
+
});
|
|
72
|
+
mk.on('drag', e => {
|
|
73
|
+
const evt = e as MarkerDragEvent<MarkerT>;
|
|
74
|
+
evt.lngLat = marker.getLngLat();
|
|
75
|
+
thisRef.current.props.onDrag?.(evt);
|
|
76
|
+
});
|
|
77
|
+
mk.on('dragend', e => {
|
|
78
|
+
const evt = e as MarkerDragEvent<MarkerT>;
|
|
79
|
+
evt.lngLat = marker.getLngLat();
|
|
80
|
+
thisRef.current.props.onDragEnd?.(evt);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return mk;
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
marker.addTo(map.getMap());
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
marker.remove();
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const {
|
|
95
|
+
longitude,
|
|
96
|
+
latitude,
|
|
97
|
+
offset,
|
|
98
|
+
style,
|
|
99
|
+
draggable = false,
|
|
100
|
+
popup = null,
|
|
101
|
+
rotation = 0,
|
|
102
|
+
rotationAlignment = 'auto',
|
|
103
|
+
pitchAlignment = 'auto'
|
|
104
|
+
} = props;
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
applyReactStyle(marker.getElement(), style);
|
|
108
|
+
}, [style]);
|
|
109
|
+
|
|
110
|
+
useImperativeHandle(ref, () => marker, []);
|
|
111
|
+
|
|
112
|
+
if (marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude) {
|
|
113
|
+
marker.setLngLat([longitude, latitude]);
|
|
114
|
+
}
|
|
115
|
+
if (offset && !arePointsEqual(marker.getOffset(), offset)) {
|
|
116
|
+
marker.setOffset(offset);
|
|
117
|
+
}
|
|
118
|
+
if (marker.isDraggable() !== draggable) {
|
|
119
|
+
marker.setDraggable(draggable);
|
|
120
|
+
}
|
|
121
|
+
if (marker.getRotation() !== rotation) {
|
|
122
|
+
marker.setRotation(rotation);
|
|
123
|
+
}
|
|
124
|
+
if (marker.getRotationAlignment() !== rotationAlignment) {
|
|
125
|
+
marker.setRotationAlignment(rotationAlignment);
|
|
126
|
+
}
|
|
127
|
+
if (marker.getPitchAlignment() !== pitchAlignment) {
|
|
128
|
+
marker.setPitchAlignment(pitchAlignment);
|
|
129
|
+
}
|
|
130
|
+
if (marker.getPopup() !== popup) {
|
|
131
|
+
marker.setPopup(popup);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return createPortal(props.children, marker.getElement());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default memo(forwardRef(Marker));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {useEffect, memo} from 'react';
|
|
3
|
+
import {applyReactStyle} from '../utils/apply-react-style';
|
|
4
|
+
import useControl from './use-control';
|
|
5
|
+
|
|
6
|
+
import type {ControlPosition, NavigationControlInstance} from '../types';
|
|
7
|
+
|
|
8
|
+
export type NavigationControlProps<OptionsT> = OptionsT & {
|
|
9
|
+
/** Placement of the control relative to the map. */
|
|
10
|
+
position?: ControlPosition;
|
|
11
|
+
/** CSS style override, applied to the control's container */
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function NavigationControl<NavigationControlOptions, ControlT extends NavigationControlInstance>(
|
|
16
|
+
props: NavigationControlProps<NavigationControlOptions>
|
|
17
|
+
): null {
|
|
18
|
+
const ctrl = useControl<ControlT>(({mapLib}) => new mapLib.NavigationControl(props) as ControlT, {
|
|
19
|
+
position: props.position
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
applyReactStyle(ctrl._container, props.style);
|
|
24
|
+
}, [props.style]);
|
|
25
|
+
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default memo(NavigationControl);
|