solid-map-gl 1.5.3 → 1.6.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.
@@ -1,39 +1,46 @@
1
1
  import { onCleanup, createEffect, untrack } from 'solid-js';
2
2
  import { useMap } from '../MapGL';
3
3
  export const Marker = props => {
4
- const map = useMap?.()();
4
+ if (!useMap())
5
+ return;
6
+ const [map] = useMap();
5
7
  let marker = null;
6
8
  let popup = null;
7
- // Remove Marker
8
- onCleanup(() => marker?.remove());
9
- // Add/Update Marker
9
+ if (!props.lngLat)
10
+ throw new Error('Marker - lngLat is required');
11
+ // Add or Update Marker
10
12
  createEffect(() => {
11
- if (untrack(() => !props.openPopup && !props.lngLat))
12
- throw new Error('Marker - lngLat is required');
13
- popup?.remove();
14
- popup = untrack(() => props.children)
15
- ? new map.mapLib.Popup(props.options.popup)
16
- .setHTML(untrack(() => props.children))
17
- .on('close', () => props.onClose?.())
18
- : null;
19
- marker?.remove();
20
- marker = new map.mapLib.Marker(props.options)
21
- .setPopup(popup)
22
- .on('dragstart', () => props.onDragStart?.())
23
- .on('dragend', () => props.onDragEnd?.())
24
- .on('drag', () => props.onLngLatChange?.(marker.getLngLat().toArray()));
13
+ const ops = { ...props.options };
14
+ const pops = { ...props.options?.popup };
15
+ if (!map())
16
+ return;
17
+ const mapboxgl = map().mapLib;
25
18
  untrack(() => {
26
- marker.setDraggable(props.draggable).setLngLat(props.lngLat).addTo(map);
27
- popup && props.openPopup !== popup.isOpen() && marker.togglePopup();
19
+ popup?.remove();
20
+ popup = props.children
21
+ ? new mapboxgl.Popup(pops)
22
+ .setHTML(props.children)
23
+ .on('close', () => props.onClose?.())
24
+ : null;
25
+ marker?.remove();
26
+ marker = new mapboxgl.Marker(ops)
27
+ .setPopup(popup)
28
+ .on('dragstart', () => props.onDragStart?.())
29
+ .on('dragend', () => props.onDragEnd?.())
30
+ .on('drag', () => props.onLngLatChange?.(marker.getLngLat().toArray()));
31
+ marker.setDraggable(props.draggable).setLngLat(props.lngLat).addTo(map());
32
+ props.openPopup !== popup?.isOpen() && marker.togglePopup();
28
33
  });
29
34
  });
30
35
  // Toggle Popup
31
- createEffect(() => popup && props.openPopup !== popup.isOpen() && marker.togglePopup());
36
+ createEffect(() => props.openPopup !== popup?.isOpen() && marker.togglePopup());
32
37
  // Update Position
33
38
  createEffect(() => marker.setLngLat(props.lngLat));
34
39
  // Update Popup Content
35
- createEffect(() => popup.setHTML(props.children));
40
+ createEffect(() => popup?.setHTML(props.children));
36
41
  // Update Draggable
37
42
  createEffect(() => marker.setDraggable(props.draggable));
43
+ // Remove Marker
44
+ onCleanup(() => marker?.remove());
38
45
  return null;
39
46
  };
@@ -1,26 +1,34 @@
1
1
  import { onCleanup, createEffect, untrack } from 'solid-js';
2
2
  import { useMap } from '../MapGL';
3
3
  export const Popup = props => {
4
- const map = useMap()();
4
+ if (!useMap())
5
+ return;
6
+ const [map] = useMap();
5
7
  let popup = null;
6
- // Remove Popup
7
- onCleanup(() => popup?.remove());
8
- // Add/Update Popup
8
+ if (!props.trackPointer && !props.lngLat)
9
+ throw new Error('Popup - lngLat or trackPointer is required');
10
+ // Add or Update Popup
9
11
  createEffect(() => {
10
- if (!props.trackPointer && !props.lngLat)
11
- throw new Error('Popup - lngLat is required');
12
- popup?.remove();
13
- popup = new map.mapLib.Popup(props.trackPointer
14
- ? { ...props.options, closeOnClick: false, closeButton: false }
15
- : { focusAfterOpen: false, ...props.options })
16
- .setHTML(untrack(() => props.children))
17
- .setLngLat(untrack(() => props.lngLat))
18
- .on('close', () => props.onClose?.())
19
- .addTo(map);
12
+ const ops = { ...props.options };
13
+ if (!map())
14
+ return;
15
+ const mapboxgl = map().mapLib;
16
+ untrack(() => {
17
+ popup?.remove();
18
+ popup = new mapboxgl.Popup(props.trackPointer
19
+ ? { ...ops, closeOnClick: false, closeButton: false }
20
+ : { focusAfterOpen: false, ...ops })
21
+ .setHTML(props.children || '')
22
+ .setLngLat(props.lngLat)
23
+ .on('close', () => props.onClose?.())
24
+ .addTo(map());
25
+ });
20
26
  });
21
27
  // Update Position
22
28
  createEffect(() => props.trackPointer ? popup.trackPointer() : popup.setLngLat(props.lngLat));
23
29
  // Update Content
24
- createEffect(() => popup.setHTML(props.children));
30
+ createEffect(() => popup.setHTML(props.children || ''));
31
+ // Remove Popup
32
+ onCleanup(() => popup?.remove());
25
33
  return null;
26
34
  };
@@ -4,8 +4,14 @@ import { rasterStyleList } from '../../mapStyles';
4
4
  const SourceContext = createContext();
5
5
  export const useSourceId = () => useContext(SourceContext);
6
6
  export const Source = props => {
7
- const map = useMap();
7
+ if (!useMap())
8
+ return;
9
+ const [map] = useMap();
8
10
  props.id = props.id || createUniqueId();
11
+ const debug = (text, value) => {
12
+ map().debug &&
13
+ console.debug('%c[MapGL]', 'color: #ec4899', text, value || '');
14
+ };
9
15
  const lookup = url => {
10
16
  const s = url?.split(':').reduce((p, c) => p && p[c], rasterStyleList);
11
17
  const source = s
@@ -25,52 +31,67 @@ export const Source = props => {
25
31
  (source.tiles = ['a', 'b', 'c'].map(i => source.tiles[0].replace('{s}', i)));
26
32
  return source;
27
33
  };
34
+ // Add Source
35
+ map().addSource(props.id, lookup(props.source.url));
36
+ map().sourceIdList.push(props.id);
37
+ debug('Add Source:', props.id);
38
+ // Update Data
39
+ const source = map().getSource(props.id);
40
+ switch (props.source.type) {
41
+ case 'geojson':
42
+ createEffect(() => {
43
+ const data = props.source.data;
44
+ if (!map().isSourceLoaded(props.id))
45
+ return;
46
+ source.setData(data || {});
47
+ debug('Update GeoJSON Data:', props.id);
48
+ });
49
+ break;
50
+ case 'image':
51
+ createEffect(() => {
52
+ const url = props.source.url;
53
+ const coords = props.source.coordinates;
54
+ if (!map().isSourceLoaded(props.id))
55
+ return;
56
+ source.updateImage(url, coords);
57
+ debug('Update Image Data:', props.id);
58
+ });
59
+ break;
60
+ case 'vector':
61
+ createEffect(() => {
62
+ const url = props.source.url;
63
+ const tiles = props.source.tiles;
64
+ if (!map().isSourceLoaded(props.id))
65
+ return;
66
+ url ? source.setUrl(url) : source.setTiles(tiles);
67
+ debug('Update Vector Data:', props.id);
68
+ });
69
+ break;
70
+ case 'raster':
71
+ createEffect(() => {
72
+ const url = props.source.url;
73
+ if (!map().isSourceLoaded(props.id))
74
+ return;
75
+ const src = lookup(url);
76
+ src.tiles &&
77
+ (src.tiles = ['a', 'b', 'c'].map(i => src.tiles[0].replace('{s}', i)));
78
+ source._tileJSONRequest?.cancel();
79
+ source.url = src.url;
80
+ source.scheme = src.scheme;
81
+ source._options = { ...source._options, ...src };
82
+ map().style._sourceCaches[`other:${props.id}`]?.clearTiles();
83
+ source.load();
84
+ debug('Update Raster Data:', props.id);
85
+ });
86
+ break;
87
+ }
28
88
  // Remove Source
29
89
  onCleanup(() => {
30
- map &&
31
- map()
32
- .getStyle()
33
- .layers.forEach(layer => layer.source === props.id && map().removeLayer(layer.id));
90
+ map()
91
+ ?.getStyle()
92
+ .layers.forEach(layer => layer.source === props.id && map().removeLayer(layer.id));
34
93
  map().removeSource(props.id);
35
- });
36
- // Update
37
- createEffect(() => {
38
- // Add Source
39
- if (!map().getSource(props.id)) {
40
- map().addSource(props.id, lookup(props.source.url));
41
- return;
42
- }
43
- // Update GeoJSON Data
44
- if (props.source.type === 'geojson' && props.source.data)
45
- map().getSource(props.id)?.setData(props.source.data);
46
- // Update Image URL
47
- if (props.source.type === 'image' &&
48
- (props.source.url || props.source.coordinates))
49
- map()
50
- .getSource(props.id)
51
- ?.updateImage(props.source.url, props.source.coordinates);
52
- // Update Vector URL or Tiles
53
- if (props.source.type === 'vector' &&
54
- (props.source.url || props.source.tiles))
55
- props.source.url
56
- ? map().getSource(props.id)?.setUrl(props.source.url)
57
- : map().getSource(props.id)?.setTiles(props.source.tiles);
58
- // Update Raster URL or Tiles
59
- if (props.source.type === 'raster' &&
60
- (props.source.url || props.source.tiles)) {
61
- let source = map().getSource(props.id);
62
- if (!source)
63
- return;
64
- const src = lookup(props.source.url);
65
- src.tiles &&
66
- (src.tiles = ['a', 'b', 'c'].map(i => src.tiles[0].replace('{s}', i)));
67
- source._tileJSONRequest?.cancel();
68
- source.url = src.url;
69
- source.scheme = src.scheme;
70
- source._options = { ...source._options, ...src };
71
- map().style._sourceCaches[`other:${props.id}`]?.clearTiles();
72
- source.load();
73
- }
94
+ debug('Remove Source:', props.id);
74
95
  });
75
96
  return (<SourceContext.Provider value={props.id}>
76
97
  {props.children}
@@ -2,38 +2,33 @@ import { onCleanup, createEffect, createUniqueId, } from 'solid-js';
2
2
  import { useMap } from '../MapGL';
3
3
  import { useSourceId } from '../Source';
4
4
  export const Terrain = props => {
5
- const map = useMap()();
5
+ if (!useMap())
6
+ return;
7
+ const [map] = useMap();
6
8
  let sourceId = useSourceId();
7
- // Remove Terrain Layer
8
- onCleanup(() => map?.setTerrain(null));
9
9
  // Add Terrain Layer
10
+ if (sourceId === undefined) {
11
+ sourceId = createUniqueId();
12
+ map().addSource(sourceId, map().isMapLibre
13
+ ? {
14
+ type: 'raster-dem',
15
+ url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
16
+ tileSize: 256,
17
+ }
18
+ : {
19
+ type: 'raster-dem',
20
+ url: 'mapbox://mapbox.terrain-rgb',
21
+ tileSize: 512,
22
+ maxzoom: 14,
23
+ });
24
+ }
10
25
  createEffect(() => {
11
- if (sourceId) {
12
- sourceId = createUniqueId();
13
- map.addSource(sourceId, map.isMapLibre
14
- ? {
15
- type: 'raster-dem',
16
- url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
17
- tileSize: 256,
18
- }
19
- : {
20
- type: 'raster-dem',
21
- url: 'mapbox://mapbox.terrain-rgb',
22
- tileSize: 512,
23
- maxzoom: 14,
24
- });
25
- }
26
- map.setTerrain({
26
+ map().setTerrain({
27
27
  exaggeration: props.exaggeration || 1,
28
28
  source: sourceId,
29
29
  });
30
30
  });
31
- // Update Visibility
32
- createEffect(() => {
33
- props.visible !== undefined &&
34
- map.setTerrain(props.visible
35
- ? { exaggeration: props.exaggeration || 1, source: sourceId }
36
- : null);
37
- });
31
+ // Remove Terrain Layer
32
+ onCleanup(() => map().setTerrain(null));
38
33
  return null;
39
34
  };
@@ -8,3 +8,4 @@ export { Marker } from './components/Marker';
8
8
  export { Popup } from './components/Popup';
9
9
  export { Image, patternList } from './components/Image';
10
10
  export { Camera } from './components/Camera';
11
+ export { Light } from './components/Light';
@@ -4,12 +4,12 @@ const gishub = 'https://raw.githubusercontent.com/GIShub4/map-styles/main/';
4
4
  const hereBase = 'https://assets.vector.hereapi.com/styles/';
5
5
  export const vectorStyleList = {
6
6
  mb: {
7
- light: `${mapBase}/light-v10`,
8
- dark: `${mapBase}/dark-v10`,
9
- street: `${mapBase}/streets-v11`,
10
- outdoor: `${mapBase}/outdoors-v11`,
7
+ light: `${mapBase}/light-v11`,
8
+ dark: `${mapBase}/dark-v11`,
9
+ street: `${mapBase}/streets-v12`,
10
+ outdoor: `${mapBase}/outdoors-v12`,
11
11
  sat: `${mapBase}/satellite-v9`,
12
- sat_street: `${mapBase}/satellite-streets-v11`,
12
+ sat_street: `${mapBase}/satellite-streets-v12`,
13
13
  nav: `${mapBase}/navigation-guidance-day-v4`,
14
14
  nav_night: `${mapBase}/navigation-guidance-night-v4`,
15
15
  basic: `${mapBase}/cjf4m44iw0uza2spb3q0a7s41`,
@@ -1,7 +1,15 @@
1
1
  import { Component } from 'solid-js';
2
2
  import type LngLatLike from 'mapbox-gl/src/geo/lng_lat.js';
3
+ interface RotateOptions {
4
+ secPerRev?: number;
5
+ maxZoom?: number;
6
+ slowZoom?: number;
7
+ pitch?: number;
8
+ around?: LngLatLike;
9
+ }
3
10
  export declare const Camera: Component<{
4
- animate: boolean;
11
+ rotateGlobe?: boolean | RotateOptions;
12
+ rotateViewport?: boolean | RotateOptions;
5
13
  translate?: {
6
14
  type?: 'line' | 'sphere';
7
15
  start: [number, number, number];
@@ -14,3 +22,4 @@ export declare const Camera: Component<{
14
22
  };
15
23
  children?: any;
16
24
  }>;
25
+ export {};
@@ -1,10 +1,10 @@
1
- import { Component } from 'solid-js';
1
+ import { VoidComponent } from 'solid-js';
2
2
  import type { Options as AttributionOptions } from 'mapbox-gl/src/ui/control/attribution_control';
3
3
  import type { Options as FullscreenOptions } from 'mapbox-gl/src/ui/control/fullscreen_control';
4
4
  import type { Options as GeolocateOptions } from 'mapbox-gl/src/ui/control/geolocate_control';
5
5
  import type { Options as NavigationOptions } from 'mapbox-gl/src/ui/control/navigation_control';
6
6
  import type { Options as ScaleOptions } from 'mapbox-gl/src/ui/control/scale_control';
7
- export declare const Control: Component<{
7
+ export declare const Control: VoidComponent<{
8
8
  type: 'navigation' | 'scale' | 'attribution' | 'fullscreen' | 'geolocate';
9
9
  options?: NavigationOptions | ScaleOptions | AttributionOptions | FullscreenOptions | GeolocateOptions | object;
10
10
  custom?: any;
@@ -0,0 +1,5 @@
1
+ import { VoidComponent } from 'solid-js';
2
+ import type { LightSpecification } from 'mapbox-gl/src/style-spec/types.js';
3
+ export declare const Light: VoidComponent<{
4
+ style: LightSpecification;
5
+ }>;
@@ -8,6 +8,10 @@ import type { StyleSpecification } from 'mapbox-gl/src/style-spec/types.js';
8
8
  import type { JSX } from 'solid-js';
9
9
  export type Viewport = {
10
10
  id?: string;
11
+ point?: {
12
+ x: number;
13
+ y: number;
14
+ };
11
15
  center?: LngLatLike;
12
16
  bounds?: LngLatBounds;
13
17
  zoom?: number;
@@ -38,6 +42,8 @@ export declare const MapGL: Component<{
38
42
  transitionType?: 'flyTo' | 'easeTo' | 'jumpTo';
39
43
  /** Event listener for Viewport updates */
40
44
  onViewportChange?: (viewport: Viewport) => void;
45
+ /** Event listener for User Interaction */
46
+ onUserInteraction?: (user: boolean) => void;
41
47
  /** Displays Map Tile Borders */
42
48
  showTileBoundaries?: boolean;
43
49
  /** Displays Wireframe if Terrain is visible */
@@ -3,5 +3,4 @@ import type { TerrainSpecification } from 'mapbox-gl/src/style-spec/types.js';
3
3
  export declare const Terrain: VoidComponent<{
4
4
  exaggeration?: number;
5
5
  source?: TerrainSpecification;
6
- visible?: boolean;
7
6
  }>;
@@ -8,5 +8,6 @@ export { Marker } from './components/Marker';
8
8
  export { Popup } from './components/Popup';
9
9
  export { Image, patternList } from './components/Image';
10
10
  export { Camera } from './components/Camera';
11
+ export { Light } from './components/Light';
11
12
  export type { Viewport } from './components/MapGL';
12
13
  export type { Color } from './components/Image';
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.3",
2
+ "version": "1.6.1",
3
3
  "name": "solid-map-gl",
4
4
  "description": "Solid Component Library for Mapbox GL JS",
5
5
  "info": "Solid Component Library for Mapbox GL JS. Mapbox GL JS is a JavaScript library that renders interactive maps from vector tiles and Mapbox styles using WebGL. This project is intended to be as close as possible to the Mapbox GL JS API.",
@@ -50,18 +50,18 @@
50
50
  "not ie 11"
51
51
  ],
52
52
  "devDependencies": {
53
+ "@rollup/plugin-terser": "^0.1.0",
53
54
  "jsdom": "^20.0.3",
54
- "mapbox-gl": "^2.11.0",
55
- "rollup": "^3.5.0",
55
+ "mapbox-gl": "^2.11.1",
56
+ "rollup": "^3.7.3",
56
57
  "rollup-plugin-import-css": "^3.1.0",
57
- "rollup-plugin-terser": "^7.0.2",
58
58
  "rollup-preset-solid": "^2.0.1",
59
- "solid-js": "^1.6.2",
59
+ "solid-js": "^1.6.4",
60
60
  "solid-testing-library": "^0.5.0",
61
- "typescript": "^4.9.3",
62
- "vite": "^3.2.4",
63
- "vite-plugin-solid": "^2.4.0",
64
- "vitest": "^0.25.3"
61
+ "typescript": "^4.9.4",
62
+ "vite": "^4.0.0",
63
+ "vite-plugin-solid": "^2.5.0",
64
+ "vitest": "^0.25.7"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "mapbox-gl": "^0.x || ^1.x || ^2.x"