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.
Files changed (138) hide show
  1. package/README.md +96 -0
  2. package/bkoi-gl/package.json +6 -0
  3. package/dist/components/attribution-control.d.ts +11 -0
  4. package/dist/components/attribution-control.js +19 -0
  5. package/dist/components/attribution-control.js.map +1 -0
  6. package/dist/components/fullscreen-control.d.ts +14 -0
  7. package/dist/components/fullscreen-control.js +21 -0
  8. package/dist/components/fullscreen-control.js.map +1 -0
  9. package/dist/components/geolocate-control.d.ts +37 -0
  10. package/dist/components/geolocate-control.js +51 -0
  11. package/dist/components/geolocate-control.js.map +1 -0
  12. package/dist/components/layer.d.ts +17 -0
  13. package/dist/components/layer.js +99 -0
  14. package/dist/components/layer.js.map +1 -0
  15. package/dist/components/map.d.ts +23 -0
  16. package/dist/components/map.js +102 -0
  17. package/dist/components/map.js.map +1 -0
  18. package/dist/components/marker.d.ts +41 -0
  19. package/dist/components/marker.js +102 -0
  20. package/dist/components/marker.js.map +1 -0
  21. package/dist/components/navigation-control.d.ts +11 -0
  22. package/dist/components/navigation-control.js +19 -0
  23. package/dist/components/navigation-control.js.map +1 -0
  24. package/dist/components/popup.d.ts +33 -0
  25. package/dist/components/popup.js +81 -0
  26. package/dist/components/popup.js.map +1 -0
  27. package/dist/components/scale-control.d.ts +13 -0
  28. package/dist/components/scale-control.js +31 -0
  29. package/dist/components/scale-control.js.map +1 -0
  30. package/dist/components/source.d.ts +7 -0
  31. package/dist/components/source.js +98 -0
  32. package/dist/components/source.js.map +1 -0
  33. package/dist/components/use-control.d.ts +9 -0
  34. package/dist/components/use-control.js +31 -0
  35. package/dist/components/use-control.js.map +1 -0
  36. package/dist/components/use-map.d.ts +20 -0
  37. package/dist/components/use-map.js +55 -0
  38. package/dist/components/use-map.js.map +1 -0
  39. package/dist/exports-bkoi-gl.cjs +1512 -0
  40. package/dist/exports-bkoi-gl.d.ts +89 -0
  41. package/dist/exports-bkoi-gl.js +36 -0
  42. package/dist/exports-bkoi-gl.js.map +1 -0
  43. package/dist/exports-mapbox.d.ts +139 -0
  44. package/dist/exports-mapbox.js +36 -0
  45. package/dist/exports-mapbox.js.map +1 -0
  46. package/dist/index.cjs +1514 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +3 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/mapbox/create-ref.d.ts +9 -0
  51. package/dist/mapbox/create-ref.js +64 -0
  52. package/dist/mapbox/create-ref.js.map +1 -0
  53. package/dist/mapbox/mapbox.d.ts +81 -0
  54. package/dist/mapbox/mapbox.js +477 -0
  55. package/dist/mapbox/mapbox.js.map +1 -0
  56. package/dist/styles/react-bkoi-gl.css +1 -0
  57. package/dist/types/common.d.ts +78 -0
  58. package/dist/types/common.js +2 -0
  59. package/dist/types/common.js.map +1 -0
  60. package/dist/types/events-bkoi-gl.d.ts +49 -0
  61. package/dist/types/events-bkoi-gl.js +2 -0
  62. package/dist/types/events-bkoi-gl.js.map +1 -0
  63. package/dist/types/events-mapbox.d.ts +49 -0
  64. package/dist/types/events-mapbox.js +2 -0
  65. package/dist/types/events-mapbox.js.map +1 -0
  66. package/dist/types/events.d.ts +39 -0
  67. package/dist/types/events.js +2 -0
  68. package/dist/types/events.js.map +1 -0
  69. package/dist/types/index.d.ts +70 -0
  70. package/dist/types/index.js +5 -0
  71. package/dist/types/index.js.map +1 -0
  72. package/dist/types/lib.d.ts +224 -0
  73. package/dist/types/lib.js +2 -0
  74. package/dist/types/lib.js.map +1 -0
  75. package/dist/types/public.d.ts +2 -0
  76. package/dist/types/public.js +2 -0
  77. package/dist/types/public.js.map +1 -0
  78. package/dist/types/style-spec-bkoi-gl.d.ts +9 -0
  79. package/dist/types/style-spec-bkoi-gl.js +2 -0
  80. package/dist/types/style-spec-bkoi-gl.js.map +1 -0
  81. package/dist/types/style-spec-mapbox.d.ts +7 -0
  82. package/dist/types/style-spec-mapbox.js +2 -0
  83. package/dist/types/style-spec-mapbox.js.map +1 -0
  84. package/dist/utils/apply-react-style.d.ts +2 -0
  85. package/dist/utils/apply-react-style.js +16 -0
  86. package/dist/utils/apply-react-style.js.map +1 -0
  87. package/dist/utils/assert.d.ts +1 -0
  88. package/dist/utils/assert.js +6 -0
  89. package/dist/utils/assert.js.map +1 -0
  90. package/dist/utils/deep-equal.d.ts +15 -0
  91. package/dist/utils/deep-equal.js +46 -0
  92. package/dist/utils/deep-equal.js.map +1 -0
  93. package/dist/utils/set-globals.d.ts +22 -0
  94. package/dist/utils/set-globals.js +19 -0
  95. package/dist/utils/set-globals.js.map +1 -0
  96. package/dist/utils/style-utils.d.ts +2 -0
  97. package/dist/utils/style-utils.js +42 -0
  98. package/dist/utils/style-utils.js.map +1 -0
  99. package/dist/utils/transform.d.ts +26 -0
  100. package/dist/utils/transform.js +56 -0
  101. package/dist/utils/transform.js.map +1 -0
  102. package/dist/utils/use-isomorphic-layout-effect.d.ts +3 -0
  103. package/dist/utils/use-isomorphic-layout-effect.js +4 -0
  104. package/dist/utils/use-isomorphic-layout-effect.js.map +1 -0
  105. package/package.json +78 -0
  106. package/src/components/attribution-control.ts +32 -0
  107. package/src/components/fullscreen-control.tsx +37 -0
  108. package/src/components/geolocate-control.ts +90 -0
  109. package/src/components/layer.ts +133 -0
  110. package/src/components/map.tsx +155 -0
  111. package/src/components/marker.ts +137 -0
  112. package/src/components/navigation-control.ts +29 -0
  113. package/src/components/popup.ts +116 -0
  114. package/src/components/scale-control.ts +46 -0
  115. package/src/components/source.ts +158 -0
  116. package/src/components/use-control.ts +64 -0
  117. package/src/components/use-map.tsx +69 -0
  118. package/src/exports-bkoi-gl.ts +156 -0
  119. package/src/exports-mapbox.ts +159 -0
  120. package/src/index.ts +2 -0
  121. package/src/mapbox/create-ref.ts +118 -0
  122. package/src/mapbox/mapbox.ts +742 -0
  123. package/src/types/common.ts +94 -0
  124. package/src/types/events-bkoi-gl.ts +78 -0
  125. package/src/types/events-mapbox.ts +76 -0
  126. package/src/types/events.ts +73 -0
  127. package/src/types/index.ts +93 -0
  128. package/src/types/lib.ts +279 -0
  129. package/src/types/public.ts +9 -0
  130. package/src/types/style-spec-bkoi-gl.ts +81 -0
  131. package/src/types/style-spec-mapbox.ts +80 -0
  132. package/src/utils/apply-react-style.ts +20 -0
  133. package/src/utils/assert.ts +5 -0
  134. package/src/utils/deep-equal.ts +61 -0
  135. package/src/utils/set-globals.ts +57 -0
  136. package/src/utils/style-utils.ts +59 -0
  137. package/src/utils/transform.ts +86 -0
  138. 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);