@movk/maplibre 1.0.0 → 1.1.0

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 (198) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/module.d.mts +7 -0
  4. package/dist/module.d.ts +7 -0
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +25 -11
  7. package/dist/runtime/components/CustomLayer.d.vue.ts +1 -1
  8. package/dist/runtime/components/CustomLayer.vue +18 -4
  9. package/dist/runtime/components/CustomLayer.vue.d.ts +1 -1
  10. package/dist/runtime/components/Layer.d.vue.ts +4 -4
  11. package/dist/runtime/components/Layer.vue +43 -18
  12. package/dist/runtime/components/Layer.vue.d.ts +4 -4
  13. package/dist/runtime/components/LayerGroup.d.vue.ts +23 -10
  14. package/dist/runtime/components/LayerGroup.vue +44 -4
  15. package/dist/runtime/components/LayerGroup.vue.d.ts +23 -10
  16. package/dist/runtime/components/LottieMarker.d.vue.ts +3 -3
  17. package/dist/runtime/components/LottieMarker.vue +6 -2
  18. package/dist/runtime/components/LottieMarker.vue.d.ts +3 -3
  19. package/dist/runtime/components/Map.d.vue.ts +2 -1
  20. package/dist/runtime/components/Map.vue +2 -6
  21. package/dist/runtime/components/Map.vue.d.ts +2 -1
  22. package/dist/runtime/components/Marker.d.vue.ts +2 -1
  23. package/dist/runtime/components/Marker.vue +33 -9
  24. package/dist/runtime/components/Marker.vue.d.ts +2 -1
  25. package/dist/runtime/components/Popup.d.vue.ts +1 -1
  26. package/dist/runtime/components/Popup.vue +23 -4
  27. package/dist/runtime/components/Popup.vue.d.ts +1 -1
  28. package/dist/runtime/components/Source.d.vue.ts +2 -2
  29. package/dist/runtime/components/Source.vue +3 -15
  30. package/dist/runtime/components/Source.vue.d.ts +2 -2
  31. package/dist/runtime/components/Tooltip.d.vue.ts +2 -2
  32. package/dist/runtime/components/Tooltip.vue +47 -19
  33. package/dist/runtime/components/Tooltip.vue.d.ts +2 -2
  34. package/dist/runtime/components/buffers/BufferCircle.d.vue.ts +1 -1
  35. package/dist/runtime/components/buffers/BufferCircle.vue.d.ts +1 -1
  36. package/dist/runtime/components/buffers/BufferEllipse.d.vue.ts +1 -1
  37. package/dist/runtime/components/buffers/BufferEllipse.vue.d.ts +1 -1
  38. package/dist/runtime/components/buffers/BufferLine.d.vue.ts +1 -1
  39. package/dist/runtime/components/buffers/BufferLine.vue.d.ts +1 -1
  40. package/dist/runtime/components/buffers/BufferPolygon.d.vue.ts +1 -1
  41. package/dist/runtime/components/buffers/BufferPolygon.vue.d.ts +1 -1
  42. package/dist/runtime/components/buffers/BufferSector.d.vue.ts +1 -1
  43. package/dist/runtime/components/buffers/BufferSector.vue.d.ts +1 -1
  44. package/dist/runtime/components/controls/BasemapControl.d.vue.ts +35 -0
  45. package/dist/runtime/components/controls/BasemapControl.vue +60 -0
  46. package/dist/runtime/components/controls/BasemapControl.vue.d.ts +35 -0
  47. package/dist/runtime/components/controls/Control.d.vue.ts +51 -0
  48. package/dist/runtime/components/controls/Control.vue +95 -0
  49. package/dist/runtime/components/controls/Control.vue.d.ts +51 -0
  50. package/dist/runtime/components/controls/GlobeControl.d.vue.ts +18 -0
  51. package/dist/runtime/components/controls/GlobeControl.vue +15 -0
  52. package/dist/runtime/components/controls/GlobeControl.vue.d.ts +18 -0
  53. package/dist/runtime/components/controls/LayerControl.d.vue.ts +44 -0
  54. package/dist/runtime/components/controls/LayerControl.vue +63 -0
  55. package/dist/runtime/components/controls/LayerControl.vue.d.ts +44 -0
  56. package/dist/runtime/components/controls/Legend.d.vue.ts +47 -0
  57. package/dist/runtime/components/controls/Legend.vue +69 -0
  58. package/dist/runtime/components/controls/Legend.vue.d.ts +47 -0
  59. package/dist/runtime/components/controls/Minimap.d.vue.ts +45 -0
  60. package/dist/runtime/components/controls/Minimap.vue +31 -0
  61. package/dist/runtime/components/controls/Minimap.vue.d.ts +45 -0
  62. package/dist/runtime/components/effects/AnimatedImage.d.vue.ts +1 -1
  63. package/dist/runtime/components/effects/AnimatedImage.vue +9 -6
  64. package/dist/runtime/components/effects/AnimatedImage.vue.d.ts +1 -1
  65. package/dist/runtime/components/effects/DiffusionCircle.d.vue.ts +1 -1
  66. package/dist/runtime/components/effects/DiffusionCircle.vue.d.ts +1 -1
  67. package/dist/runtime/components/effects/FlowBuilding.d.vue.ts +1 -1
  68. package/dist/runtime/components/effects/FlowBuilding.vue.d.ts +1 -1
  69. package/dist/runtime/components/effects/GlowCircle.d.vue.ts +1 -1
  70. package/dist/runtime/components/effects/GlowCircle.vue.d.ts +1 -1
  71. package/dist/runtime/components/effects/GradientBuilding.d.vue.ts +1 -1
  72. package/dist/runtime/components/effects/GradientBuilding.vue.d.ts +1 -1
  73. package/dist/runtime/components/effects/Migration.d.vue.ts +1 -1
  74. package/dist/runtime/components/effects/Migration.vue.d.ts +1 -1
  75. package/dist/runtime/components/effects/Radar.d.vue.ts +1 -1
  76. package/dist/runtime/components/effects/Radar.vue.d.ts +1 -1
  77. package/dist/runtime/components/effects/SpriteImage.d.vue.ts +1 -1
  78. package/dist/runtime/components/effects/SpriteImage.vue +40 -24
  79. package/dist/runtime/components/effects/SpriteImage.vue.d.ts +1 -1
  80. package/dist/runtime/components/effects/TextureBuilding.d.vue.ts +1 -1
  81. package/dist/runtime/components/effects/TextureBuilding.vue +1 -1
  82. package/dist/runtime/components/effects/TextureBuilding.vue.d.ts +1 -1
  83. package/dist/runtime/components/effects/Trail.d.vue.ts +1 -1
  84. package/dist/runtime/components/effects/Trail.vue.d.ts +1 -1
  85. package/dist/runtime/components/effects/WaveCircle.d.vue.ts +1 -1
  86. package/dist/runtime/components/effects/WaveCircle.vue.d.ts +1 -1
  87. package/dist/runtime/components/effects/WindowBuilding.d.vue.ts +1 -1
  88. package/dist/runtime/components/effects/WindowBuilding.vue.d.ts +1 -1
  89. package/dist/runtime/components/environment/Projection.d.vue.ts +28 -0
  90. package/dist/runtime/components/environment/Projection.vue +27 -0
  91. package/dist/runtime/components/environment/Projection.vue.d.ts +28 -0
  92. package/dist/runtime/components/environment/Temperature.d.vue.ts +1 -1
  93. package/dist/runtime/components/environment/Temperature.vue.d.ts +1 -1
  94. package/dist/runtime/components/environment/Terrain.d.vue.ts +1 -1
  95. package/dist/runtime/components/environment/Terrain.vue +5 -0
  96. package/dist/runtime/components/environment/Terrain.vue.d.ts +1 -1
  97. package/dist/runtime/components/extensions/DrawControl.vue +23 -19
  98. package/dist/runtime/components/extensions/Swipe.d.vue.ts +48 -0
  99. package/dist/runtime/components/extensions/Swipe.vue +141 -0
  100. package/dist/runtime/components/extensions/Swipe.vue.d.ts +48 -0
  101. package/dist/runtime/components/layers/BuildingLayer.d.vue.ts +1 -1
  102. package/dist/runtime/components/layers/BuildingLayer.vue.d.ts +1 -1
  103. package/dist/runtime/components/layers/ClusterLayer.d.vue.ts +1 -1
  104. package/dist/runtime/components/layers/ClusterLayer.vue.d.ts +1 -1
  105. package/dist/runtime/components/layers/ContourLayer.d.vue.ts +59 -0
  106. package/dist/runtime/components/layers/ContourLayer.vue +84 -0
  107. package/dist/runtime/components/layers/ContourLayer.vue.d.ts +59 -0
  108. package/dist/runtime/components/layers/GraticuleLayer.d.vue.ts +31 -0
  109. package/dist/runtime/components/layers/GraticuleLayer.vue +88 -0
  110. package/dist/runtime/components/layers/GraticuleLayer.vue.d.ts +31 -0
  111. package/dist/runtime/components/layers/ImageLayer.d.vue.ts +1 -1
  112. package/dist/runtime/components/layers/ImageLayer.vue.d.ts +1 -1
  113. package/dist/runtime/components/layers/MaskLayer.d.vue.ts +44 -0
  114. package/dist/runtime/components/layers/MaskLayer.vue +32 -0
  115. package/dist/runtime/components/layers/MaskLayer.vue.d.ts +44 -0
  116. package/dist/runtime/components/layers/RasterLayer.d.vue.ts +5 -3
  117. package/dist/runtime/components/layers/RasterLayer.vue +9 -2
  118. package/dist/runtime/components/layers/RasterLayer.vue.d.ts +5 -3
  119. package/dist/runtime/components/layers/VideoLayer.d.vue.ts +1 -1
  120. package/dist/runtime/components/layers/VideoLayer.vue.d.ts +1 -1
  121. package/dist/runtime/composables/useFeatureState.js +27 -5
  122. package/dist/runtime/composables/useFrameIcon.d.ts +4 -3
  123. package/dist/runtime/composables/useFrameIcon.js +18 -8
  124. package/dist/runtime/composables/useLayerTree.d.ts +11 -0
  125. package/dist/runtime/composables/useLayerTree.js +12 -0
  126. package/dist/runtime/composables/useMapAnimation.d.ts +4 -2
  127. package/dist/runtime/composables/useMapAnimation.js +5 -4
  128. package/dist/runtime/composables/useMapExport.js +2 -5
  129. package/dist/runtime/composables/useMapSync.d.ts +16 -0
  130. package/dist/runtime/composables/useMapSync.js +42 -0
  131. package/dist/runtime/composables/useMaplibreCamera.d.ts +1 -1
  132. package/dist/runtime/composables/useMaplibreCamera.js +1 -1
  133. package/dist/runtime/composables/useMaplibreImage.d.ts +3 -3
  134. package/dist/runtime/composables/useMaplibreImage.js +26 -5
  135. package/dist/runtime/domains/map/config.js +2 -3
  136. package/dist/runtime/domains/map/context.js +15 -0
  137. package/dist/runtime/domains/map/control.d.ts +1 -1
  138. package/dist/runtime/domains/map/control.js +1 -1
  139. package/dist/runtime/domains/map/draw-modes.d.ts +9 -0
  140. package/dist/runtime/domains/map/draw-modes.js +53 -0
  141. package/dist/runtime/domains/map/draw-registry.d.ts +2 -3
  142. package/dist/runtime/domains/map/draw-registry.js +6 -9
  143. package/dist/runtime/domains/map/draw-theme.d.ts +12 -0
  144. package/dist/runtime/domains/map/draw-toolbar.js +11 -10
  145. package/dist/runtime/domains/map/layer-group.d.ts +15 -4
  146. package/dist/runtime/domains/map/layer-tree.d.ts +3 -0
  147. package/dist/runtime/domains/map/layer-tree.js +10 -0
  148. package/dist/runtime/domains/map/minimap.d.ts +18 -0
  149. package/dist/runtime/domains/map/minimap.js +86 -0
  150. package/dist/runtime/domains/map/registry.js +4 -3
  151. package/dist/runtime/domains/map/style-layers.d.ts +7 -0
  152. package/dist/runtime/domains/map/style-layers.js +54 -0
  153. package/dist/runtime/domains/map/style.d.ts +3 -0
  154. package/dist/runtime/domains/map/style.js +5 -0
  155. package/dist/runtime/domains/map/swipe.d.ts +9 -0
  156. package/dist/runtime/domains/map/swipe.js +62 -0
  157. package/dist/runtime/index.css +1 -1
  158. package/dist/runtime/types/draw.d.ts +34 -1
  159. package/dist/runtime/types/index.d.ts +1 -0
  160. package/dist/runtime/types/layer.d.ts +40 -0
  161. package/dist/runtime/types/layer.js +0 -0
  162. package/dist/runtime/types/map.d.ts +6 -0
  163. package/dist/runtime/utils/cog.d.ts +2 -0
  164. package/dist/runtime/utils/cog.js +9 -0
  165. package/dist/runtime/utils/effects.js +2 -1
  166. package/dist/runtime/utils/events.d.ts +6 -0
  167. package/dist/runtime/utils/events.js +25 -0
  168. package/dist/runtime/utils/frame-icon.d.ts +2 -5
  169. package/dist/runtime/utils/frame-icon.js +10 -4
  170. package/dist/runtime/utils/graticule.d.ts +19 -0
  171. package/dist/runtime/utils/graticule.js +62 -0
  172. package/dist/runtime/utils/layer-opacity.d.ts +7 -0
  173. package/dist/runtime/utils/layer-opacity.js +59 -0
  174. package/dist/runtime/utils/layer.js +2 -1
  175. package/dist/runtime/utils/legend.d.ts +9 -0
  176. package/dist/runtime/utils/legend.js +52 -0
  177. package/dist/runtime/utils/marker.d.ts +11 -0
  178. package/dist/runtime/utils/marker.js +28 -0
  179. package/dist/runtime/utils/mask.d.ts +8 -0
  180. package/dist/runtime/utils/mask.js +33 -0
  181. package/dist/runtime/utils/minimap.d.ts +6 -0
  182. package/dist/runtime/utils/minimap.js +15 -0
  183. package/dist/runtime/utils/pmtiles.d.ts +3 -0
  184. package/dist/runtime/utils/pmtiles.js +10 -0
  185. package/dist/runtime/utils/source.d.ts +7 -0
  186. package/dist/runtime/utils/source.js +35 -0
  187. package/dist/runtime/utils/swipe.d.ts +32 -0
  188. package/dist/runtime/utils/swipe.js +18 -0
  189. package/dist/runtime/utils/tianditu.d.ts +10 -1
  190. package/dist/runtime/utils/tianditu.js +11 -0
  191. package/dist/unplugin.d.mts +1 -1
  192. package/dist/unplugin.d.ts +1 -1
  193. package/dist/unplugin.mjs +1 -7
  194. package/package.json +20 -12
  195. package/dist/runtime/draw-modes/index.d.ts +0 -16
  196. package/dist/runtime/draw-modes/index.js +0 -32
  197. package/dist/runtime/utils/draw-theme.d.ts +0 -47
  198. /package/dist/runtime/{utils → domains/map}/draw-theme.js +0 -0
@@ -2,6 +2,8 @@
2
2
  import { onMounted, onUnmounted, ref, useSlots, useTemplateRef, watch } from "vue";
3
3
  import { Marker } from "maplibre-gl";
4
4
  import { useMap } from "../composables/useMap";
5
+ import { isDeepEqual } from "@movk/core";
6
+ import { applyMarkerOptions, markerNeedsRebuild, removeMarkerClassName } from "../utils/marker";
5
7
  import MaplibrePopup from "./Popup.vue";
6
8
  const props = defineProps({
7
9
  options: { type: Object, required: false },
@@ -15,7 +17,9 @@ const slots = useSlots();
15
17
  const el = useTemplateRef("el");
16
18
  const ready = ref(false);
17
19
  let marker;
20
+ let applied = {};
18
21
  let unbindTrigger;
22
+ let disposed = false;
19
23
  function openPopup() {
20
24
  open.value = true;
21
25
  }
@@ -46,25 +50,45 @@ function bindTrigger() {
46
50
  element.removeEventListener("mouseleave", closePopup);
47
51
  };
48
52
  }
49
- onMounted(async () => {
50
- const map = await ctx.whenLoaded();
53
+ function onDragEnd() {
54
+ if (!marker) return;
55
+ const { lng, lat } = marker.getLngLat();
56
+ lnglat.value = [lng, lat];
57
+ }
58
+ function createMarker(map) {
51
59
  const useSlot = Boolean(slots.default && el.value);
52
- marker = new Marker({ ...props.options, ...useSlot ? { element: el.value } : {} });
60
+ applied = { ...props.options };
61
+ marker = new Marker({ ...applied, ...useSlot ? { element: el.value } : {} });
53
62
  marker.setLngLat(lnglat.value).addTo(map);
54
- ready.value = true;
63
+ marker.on("dragend", onDragEnd);
55
64
  bindTrigger();
56
- if (props.options?.draggable) {
57
- marker.on("dragend", () => {
58
- const { lng, lat } = marker.getLngLat();
59
- lnglat.value = [lng, lat];
60
- });
65
+ }
66
+ onMounted(async () => {
67
+ const map = await ctx.whenAttached();
68
+ if (disposed) return;
69
+ createMarker(map);
70
+ ready.value = true;
71
+ });
72
+ watch(() => props.options, (value) => {
73
+ const map = ctx.map.value;
74
+ const next = value ?? {};
75
+ if (!marker || !map || isDeepEqual(next, applied)) return;
76
+ if (markerNeedsRebuild(next, applied)) {
77
+ removeMarkerClassName(marker, applied.className);
78
+ unbindTrigger?.();
79
+ marker.remove();
80
+ createMarker(map);
81
+ return;
61
82
  }
83
+ applyMarkerOptions(marker, next, applied);
84
+ applied = { ...next };
62
85
  });
63
86
  watch(lnglat, (value) => {
64
87
  if (value) marker?.setLngLat(value);
65
88
  });
66
89
  watch(() => props.trigger, bindTrigger);
67
90
  onUnmounted(() => {
91
+ disposed = true;
68
92
  unbindTrigger?.();
69
93
  marker?.remove();
70
94
  });
@@ -3,7 +3,8 @@ import type { LngLatLike, MarkerOptions, PopupOptions } from 'maplibre-gl';
3
3
  import type { PopupTrigger } from '../types';
4
4
  type __VLS_Props = {
5
5
  /**
6
- * 标记选项;element 由默认插槽提供,无需在此传入
6
+ * 标记选项;element 由默认插槽提供,无需在此传入。
7
+ * 有 setter 的字段(rotation、draggable 等)增量更新,anchor、color、scale 变化时重建
7
8
  * @see https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MarkerOptions/
8
9
  */
9
10
  options?: Omit<MarkerOptions, 'element'>;
@@ -4,7 +4,7 @@ type __VLS_Props = {
4
4
  /** 弹窗锚定的经纬度 */
5
5
  lnglat: LngLatLike;
6
6
  /**
7
- * Popup 选项
7
+ * Popup 选项;值变化时重建弹窗
8
8
  * @see https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/PopupOptions/
9
9
  */
10
10
  options?: PopupOptions;
@@ -2,6 +2,7 @@
2
2
  import { onMounted, onUnmounted, useTemplateRef, watch } from "vue";
3
3
  import { Popup } from "maplibre-gl";
4
4
  import { useMap } from "../composables/useMap";
5
+ import { isDeepEqual } from "@movk/core";
5
6
  const props = defineProps({
6
7
  lnglat: { type: [Object, Array], required: true },
7
8
  options: { type: Object, required: false }
@@ -11,16 +12,34 @@ defineOptions({ inheritAttrs: false });
11
12
  const ctx = useMap();
12
13
  const el = useTemplateRef("el");
13
14
  let popup;
14
- onMounted(async () => {
15
- const map = await ctx.whenLoaded();
15
+ let disposed = false;
16
+ function onClose() {
17
+ emit("close");
18
+ }
19
+ function createPopup(map) {
16
20
  popup = new Popup(props.options);
17
21
  popup.setLngLat(props.lnglat);
18
22
  if (el.value) popup.setDOMContent(el.value);
19
- popup.on("close", () => emit("close"));
23
+ popup.on("close", onClose);
20
24
  popup.addTo(map);
25
+ }
26
+ onMounted(async () => {
27
+ const map = await ctx.whenAttached();
28
+ if (disposed) return;
29
+ createPopup(map);
21
30
  });
22
31
  watch(() => props.lnglat, (value) => popup?.setLngLat(value));
23
- onUnmounted(() => popup?.remove());
32
+ watch(() => props.options, (next, prev) => {
33
+ const map = ctx.map.value;
34
+ if (!popup || !map || isDeepEqual(next, prev)) return;
35
+ popup.off("close", onClose);
36
+ popup.remove();
37
+ createPopup(map);
38
+ });
39
+ onUnmounted(() => {
40
+ disposed = true;
41
+ popup?.remove();
42
+ });
24
43
  defineExpose({ popup: () => popup });
25
44
  </script>
26
45
 
@@ -4,7 +4,7 @@ type __VLS_Props = {
4
4
  /** 弹窗锚定的经纬度 */
5
5
  lnglat: LngLatLike;
6
6
  /**
7
- * Popup 选项
7
+ * Popup 选项;值变化时重建弹窗
8
8
  * @see https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/PopupOptions/
9
9
  */
10
10
  options?: PopupOptions;
@@ -1,9 +1,9 @@
1
1
  import type { SourceSpecification } from '@maplibre/maplibre-gl-style-spec';
2
2
  type __VLS_Props = {
3
- /** 数据源 id,供图层经 source 字段按字符串引用 */
3
+ /** 数据源 id,供图层经 source 字段按字符串引用;变更需配合 `:key` 重建 */
4
4
  sourceId: string;
5
5
  /**
6
- * 数据源定义
6
+ * 数据源定义,变化时按类型增量更新(setData / setTiles 等)
7
7
  * @see https://maplibre.org/maplibre-style-spec/sources/
8
8
  */
9
9
  source: SourceSpecification;
@@ -1,6 +1,7 @@
1
1
  <script setup>
2
2
  import { onUnmounted, watch } from "vue";
3
3
  import { useMap } from "../composables/useMap";
4
+ import { updateSource } from "../utils/source";
4
5
  const props = defineProps({
5
6
  sourceId: { type: String, required: true },
6
7
  source: { type: Object, required: true }
@@ -11,22 +12,9 @@ const stopReady = ctx.onReady((map) => {
11
12
  map.addSource(props.sourceId, props.source);
12
13
  }
13
14
  });
14
- watch(() => props.source, (next) => {
15
+ watch(() => props.source, (next, prev) => {
15
16
  const source = ctx.map.value?.getSource(props.sourceId);
16
- if (!source) return;
17
- if (next.type === "geojson" && next.data) {
18
- source.setData(next.data);
19
- } else if (next.type === "vector") {
20
- if (next.url) source.setUrl(next.url);
21
- if (next.tiles) source.setTiles(next.tiles);
22
- } else if (next.type === "raster" || next.type === "raster-dem") {
23
- if (next.url) source.setUrl(next.url);
24
- if (next.tiles) source.setTiles(next.tiles);
25
- } else if (next.type === "image" && next.url) {
26
- source.updateImage(next);
27
- } else if (next.type === "video" && next.coordinates) {
28
- source.setCoordinates(next.coordinates);
29
- }
17
+ if (source) updateSource(source, next, prev);
30
18
  }, { deep: true });
31
19
  onUnmounted(() => {
32
20
  stopReady();
@@ -1,9 +1,9 @@
1
1
  import type { SourceSpecification } from '@maplibre/maplibre-gl-style-spec';
2
2
  type __VLS_Props = {
3
- /** 数据源 id,供图层经 source 字段按字符串引用 */
3
+ /** 数据源 id,供图层经 source 字段按字符串引用;变更需配合 `:key` 重建 */
4
4
  sourceId: string;
5
5
  /**
6
- * 数据源定义
6
+ * 数据源定义,变化时按类型增量更新(setData / setTiles 等)
7
7
  * @see https://maplibre.org/maplibre-style-spec/sources/
8
8
  */
9
9
  source: SourceSpecification;
@@ -2,10 +2,10 @@ import type { GeoJSONFeature, PopupOptions } from 'maplibre-gl';
2
2
  import type { PopupTrigger } from '../types';
3
3
  /** 目标图层要素的弹窗:按 trigger 以悬浮或点击触发,作用域插槽拿到当前要素。 */
4
4
  type __VLS_Props = {
5
- /** 目标图层 id */
5
+ /** 目标图层 id;变化时改绑到新图层 */
6
6
  layerId: string;
7
7
  /**
8
- * Popup 选项(hover 模式下 closeButton/closeOnClick 由组件接管)
8
+ * Popup 选项(hover 模式下 closeButton/closeOnClick 由组件接管);值变化时重建弹窗
9
9
  * @see https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/PopupOptions/
10
10
  */
11
11
  options?: PopupOptions;
@@ -2,6 +2,8 @@
2
2
  import { onMounted, onUnmounted, shallowRef, useTemplateRef, watch } from "vue";
3
3
  import { Popup } from "maplibre-gl";
4
4
  import { useMap } from "../composables/useMap";
5
+ import { isDeepEqual } from "@movk/core";
6
+ import { onLayerDataChange } from "../utils/events";
5
7
  const props = defineProps({
6
8
  layerId: { type: String, required: true },
7
9
  options: { type: Object, required: false },
@@ -12,22 +14,35 @@ const ctx = useMap();
12
14
  const el = useTemplateRef("el");
13
15
  const hovered = shallowRef();
14
16
  let popup;
17
+ let anchor;
15
18
  let unbind;
19
+ let disposed = false;
16
20
  function setCursor(value) {
17
21
  const map = ctx.map.value;
18
22
  if (map) map.getCanvas().style.cursor = value;
19
23
  }
20
- function show(event) {
24
+ function open(lngLat, feature) {
21
25
  const map = ctx.map.value;
22
26
  if (!map || !popup || !el.value) return;
23
- hovered.value = event.features?.[0];
24
- popup.setLngLat(event.lngLat).setDOMContent(el.value);
27
+ hovered.value = feature;
28
+ anchor = lngLat;
29
+ popup.setLngLat(lngLat).setDOMContent(el.value);
25
30
  if (!popup.isOpen()) popup.addTo(map);
26
31
  }
32
+ function show(event) {
33
+ open(event.lngLat, event.features?.[0]);
34
+ }
27
35
  function close() {
28
36
  hovered.value = void 0;
37
+ anchor = void 0;
29
38
  popup?.remove();
30
39
  }
40
+ function revalidate(map) {
41
+ if (!popup?.isOpen() || !anchor) return;
42
+ const [feature] = map.queryRenderedFeatures(map.project(anchor), { layers: [props.layerId] });
43
+ if (feature) hovered.value = feature;
44
+ else close();
45
+ }
31
46
  function onMove(event) {
32
47
  setCursor(props.cursor);
33
48
  show(event);
@@ -50,22 +65,26 @@ function popupOptions() {
50
65
  }
51
66
  function bind(map) {
52
67
  if (props.trigger === "none") return;
68
+ const { layerId } = props;
69
+ const stopDataChange = onLayerDataChange(map, layerId, () => revalidate(map));
53
70
  if (props.trigger === "hover") {
54
- map.on("mousemove", props.layerId, onMove);
55
- map.on("mouseleave", props.layerId, onLeave);
71
+ map.on("mousemove", layerId, onMove);
72
+ map.on("mouseleave", layerId, onLeave);
56
73
  unbind = () => {
57
- map.off("mousemove", props.layerId, onMove);
58
- map.off("mouseleave", props.layerId, onLeave);
74
+ stopDataChange();
75
+ map.off("mousemove", layerId, onMove);
76
+ map.off("mouseleave", layerId, onLeave);
59
77
  };
60
78
  return;
61
79
  }
62
- map.on("click", props.layerId, onClick);
63
- map.on("mouseenter", props.layerId, onEnter);
64
- map.on("mouseleave", props.layerId, onLeaveCursor);
80
+ map.on("click", layerId, onClick);
81
+ map.on("mouseenter", layerId, onEnter);
82
+ map.on("mouseleave", layerId, onLeaveCursor);
65
83
  unbind = () => {
66
- map.off("click", props.layerId, onClick);
67
- map.off("mouseenter", props.layerId, onEnter);
68
- map.off("mouseleave", props.layerId, onLeaveCursor);
84
+ stopDataChange();
85
+ map.off("click", layerId, onClick);
86
+ map.off("mouseenter", layerId, onEnter);
87
+ map.off("mouseleave", layerId, onLeaveCursor);
69
88
  };
70
89
  }
71
90
  function teardown() {
@@ -81,16 +100,25 @@ function setup(map) {
81
100
  bind(map);
82
101
  }
83
102
  onMounted(async () => {
84
- const map = await ctx.whenLoaded();
103
+ const map = await ctx.whenAttached();
104
+ if (disposed) return;
85
105
  setup(map);
86
106
  });
87
- watch(() => props.trigger, () => {
88
- const map = ctx.map.value;
89
- if (!map) return;
107
+ watch(
108
+ [() => props.trigger, () => props.layerId, () => props.options],
109
+ ([trigger, layerId, options], [prevTrigger, prevLayerId, prevOptions]) => {
110
+ const map = ctx.map.value;
111
+ if (!map || !popup || trigger === prevTrigger && layerId === prevLayerId && isDeepEqual(options, prevOptions)) return;
112
+ const restore = trigger === "click" && trigger === prevTrigger && layerId === prevLayerId && popup.isOpen() && anchor ? { lngLat: anchor, feature: hovered.value } : void 0;
113
+ teardown();
114
+ setup(map);
115
+ if (restore) open(restore.lngLat, restore.feature);
116
+ }
117
+ );
118
+ onUnmounted(() => {
119
+ disposed = true;
90
120
  teardown();
91
- setup(map);
92
121
  });
93
- onUnmounted(teardown);
94
122
  defineExpose({
95
123
  /** 当前激活要素 */
96
124
  hovered: () => hovered.value
@@ -2,10 +2,10 @@ import type { GeoJSONFeature, PopupOptions } from 'maplibre-gl';
2
2
  import type { PopupTrigger } from '../types';
3
3
  /** 目标图层要素的弹窗:按 trigger 以悬浮或点击触发,作用域插槽拿到当前要素。 */
4
4
  type __VLS_Props = {
5
- /** 目标图层 id */
5
+ /** 目标图层 id;变化时改绑到新图层 */
6
6
  layerId: string;
7
7
  /**
8
- * Popup 选项(hover 模式下 closeButton/closeOnClick 由组件接管)
8
+ * Popup 选项(hover 模式下 closeButton/closeOnClick 由组件接管);值变化时重建弹窗
9
9
  * @see https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/PopupOptions/
10
10
  */
11
11
  options?: PopupOptions;
@@ -9,7 +9,7 @@ type __VLS_Props = {
9
9
  * @defaultValue 64
10
10
  */
11
11
  steps?: number;
12
- /** 图层 id 前缀;省略时自动生成 */
12
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
13
13
  layerId?: string;
14
14
  /** 主色(快捷样式) */
15
15
  color?: string;
@@ -9,7 +9,7 @@ type __VLS_Props = {
9
9
  * @defaultValue 64
10
10
  */
11
11
  steps?: number;
12
- /** 图层 id 前缀;省略时自动生成 */
12
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
13
13
  layerId?: string;
14
14
  /** 主色(快捷样式) */
15
15
  color?: string;
@@ -16,7 +16,7 @@ type __VLS_Props = {
16
16
  * @defaultValue 64
17
17
  */
18
18
  steps?: number;
19
- /** 图层 id 前缀;省略时自动生成 */
19
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
20
20
  layerId?: string;
21
21
  /** 主色(快捷样式) */
22
22
  color?: string;
@@ -16,7 +16,7 @@ type __VLS_Props = {
16
16
  * @defaultValue 64
17
17
  */
18
18
  steps?: number;
19
- /** 图层 id 前缀;省略时自动生成 */
19
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
20
20
  layerId?: string;
21
21
  /** 主色(快捷样式) */
22
22
  color?: string;
@@ -5,7 +5,7 @@ type __VLS_Props = {
5
5
  line: Feature<LineString> | LineString | [number, number][];
6
6
  /** 缓冲宽度(米) */
7
7
  width: number;
8
- /** 图层 id 前缀;省略时自动生成 */
8
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
9
9
  layerId?: string;
10
10
  /** 主色(快捷样式) */
11
11
  color?: string;
@@ -5,7 +5,7 @@ type __VLS_Props = {
5
5
  line: Feature<LineString> | LineString | [number, number][];
6
6
  /** 缓冲宽度(米) */
7
7
  width: number;
8
- /** 图层 id 前缀;省略时自动生成 */
8
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
9
9
  layerId?: string;
10
10
  /** 主色(快捷样式) */
11
11
  color?: string;
@@ -5,7 +5,7 @@ type __VLS_Props = {
5
5
  polygon: Feature<Polygon> | Polygon | [number, number][];
6
6
  /** 缓冲宽度(米),负值向内收缩 */
7
7
  width: number;
8
- /** 图层 id 前缀;省略时自动生成 */
8
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
9
9
  layerId?: string;
10
10
  /** 主色(快捷样式) */
11
11
  color?: string;
@@ -5,7 +5,7 @@ type __VLS_Props = {
5
5
  polygon: Feature<Polygon> | Polygon | [number, number][];
6
6
  /** 缓冲宽度(米),负值向内收缩 */
7
7
  width: number;
8
- /** 图层 id 前缀;省略时自动生成 */
8
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
9
9
  layerId?: string;
10
10
  /** 主色(快捷样式) */
11
11
  color?: string;
@@ -13,7 +13,7 @@ type __VLS_Props = {
13
13
  * @defaultValue 64
14
14
  */
15
15
  steps?: number;
16
- /** 图层 id 前缀;省略时自动生成 */
16
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
17
17
  layerId?: string;
18
18
  /** 主色(快捷样式) */
19
19
  color?: string;
@@ -13,7 +13,7 @@ type __VLS_Props = {
13
13
  * @defaultValue 64
14
14
  */
15
15
  steps?: number;
16
- /** 图层 id 前缀;省略时自动生成 */
16
+ /** 图层 id 前缀;省略时自动生成,变更需配合 `:key` 重建 */
17
17
  layerId?: string;
18
18
  /** 主色(快捷样式) */
19
19
  color?: string;
@@ -0,0 +1,35 @@
1
+ import type { ControlPosition, StyleSpecification } from 'maplibre-gl';
2
+ import type { BasemapItem } from '../../types';
3
+ /**
4
+ * 底图切换:受控组件,只发出选中项的 style,不直接调用 setStyle。
5
+ * 将同一个值绑定到 MaplibreMap 的 options.style,由地图负责切换并重建叠加图层。
6
+ */
7
+ type __VLS_Props = {
8
+ /** 候选底图 */
9
+ items: BasemapItem[];
10
+ /** 控件停靠位置;省略用地图默认位置 */
11
+ position?: ControlPosition;
12
+ /**
13
+ * 折叠按钮与选项组的无障碍标签
14
+ * @defaultValue 'Basemap'
15
+ */
16
+ label?: string;
17
+ };
18
+ type __VLS_ModelProps = {
19
+ /** 当前底图样式,与 MaplibreMap 的 options.style 绑定同一个值;按引用或字符串相等判断选中项 */
20
+ modelValue?: StyleSpecification | string;
21
+ /** 面板是否展开 */
22
+ 'open'?: boolean;
23
+ };
24
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
25
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
26
+ "update:open": (value: boolean) => any;
27
+ "update:modelValue": (value: string | StyleSpecification | undefined) => any;
28
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
29
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
30
+ "onUpdate:modelValue"?: ((value: string | StyleSpecification | undefined) => any) | undefined;
31
+ }>, {
32
+ label: string;
33
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
+ declare const _default: typeof __VLS_export;
35
+ export default _default;
@@ -0,0 +1,60 @@
1
+ <script setup>
2
+ import { computed, toRaw } from "vue";
3
+ import MaplibreControl from "./Control.vue";
4
+ const props = defineProps({
5
+ items: { type: Array, required: true },
6
+ position: { type: String, required: false },
7
+ label: { type: String, required: false, default: "Basemap" }
8
+ });
9
+ const style = defineModel({ type: [Object, String] });
10
+ const open = defineModel("open", { type: Boolean, ...{ default: false } });
11
+ const grid = computed(() => props.items.some((item) => item.thumbnail));
12
+ function isSelected(item) {
13
+ return style.value !== void 0 && toRaw(item.style) === toRaw(style.value);
14
+ }
15
+ </script>
16
+
17
+ <template>
18
+ <MaplibreControl
19
+ v-model:open="open"
20
+ :position="position"
21
+ :label="label"
22
+ collapsible
23
+ >
24
+ <template #trigger>
25
+ <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
26
+ <path
27
+ fill="none"
28
+ stroke="currentColor"
29
+ stroke-linecap="round"
30
+ stroke-linejoin="round"
31
+ stroke-width="2"
32
+ d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0zm.894.211v15M9 3.236v15"
33
+ />
34
+ </svg>
35
+ </template>
36
+ <div
37
+ class="movk-maplibre-basemap-control"
38
+ :class="{ 'movk-maplibre-basemap-control--grid': grid }"
39
+ role="radiogroup"
40
+ :aria-label="label"
41
+ >
42
+ <button
43
+ v-for="item in items"
44
+ :key="item.label"
45
+ type="button"
46
+ role="radio"
47
+ class="movk-maplibre-basemap-control__item"
48
+ :aria-checked="isSelected(item)"
49
+ @click="style = item.style"
50
+ >
51
+ <img v-if="item.thumbnail" :src="item.thumbnail" alt="" class="movk-maplibre-basemap-control__thumbnail">
52
+ <span>{{ item.label }}</span>
53
+ </button>
54
+ </div>
55
+ </MaplibreControl>
56
+ </template>
57
+
58
+ <style>
59
+ :where(.movk-maplibre-basemap-control){display:flex;flex-direction:column;gap:2px;min-width:120px}:where(.movk-maplibre-basemap-control--grid){display:grid;gap:6px;grid-template-columns:repeat(2,72px)}.movk-maplibre-control .movk-maplibre-basemap-control__item{align-items:flex-start;background:transparent;border:1px solid transparent;border-radius:4px;color:inherit;cursor:pointer;display:flex;flex-direction:column;font:inherit;gap:2px;height:auto;padding:3px 6px;text-align:left;width:100%}.movk-maplibre-control .movk-maplibre-basemap-control__item:hover{background:rgba(0,0,0,.05)}.movk-maplibre-control .movk-maplibre-basemap-control__item[aria-checked=true]{border-color:#3b82f6;color:#1d4ed8}.movk-maplibre-control .movk-maplibre-basemap-control--grid .movk-maplibre-basemap-control__item{align-items:center;padding:3px}:where(.movk-maplibre-basemap-control__thumbnail){border-radius:3px;height:48px;-o-object-fit:cover;object-fit:cover;width:64px}
60
+ </style>
@@ -0,0 +1,35 @@
1
+ import type { ControlPosition, StyleSpecification } from 'maplibre-gl';
2
+ import type { BasemapItem } from '../../types';
3
+ /**
4
+ * 底图切换:受控组件,只发出选中项的 style,不直接调用 setStyle。
5
+ * 将同一个值绑定到 MaplibreMap 的 options.style,由地图负责切换并重建叠加图层。
6
+ */
7
+ type __VLS_Props = {
8
+ /** 候选底图 */
9
+ items: BasemapItem[];
10
+ /** 控件停靠位置;省略用地图默认位置 */
11
+ position?: ControlPosition;
12
+ /**
13
+ * 折叠按钮与选项组的无障碍标签
14
+ * @defaultValue 'Basemap'
15
+ */
16
+ label?: string;
17
+ };
18
+ type __VLS_ModelProps = {
19
+ /** 当前底图样式,与 MaplibreMap 的 options.style 绑定同一个值;按引用或字符串相等判断选中项 */
20
+ modelValue?: StyleSpecification | string;
21
+ /** 面板是否展开 */
22
+ 'open'?: boolean;
23
+ };
24
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
25
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
26
+ "update:open": (value: boolean) => any;
27
+ "update:modelValue": (value: string | StyleSpecification | undefined) => any;
28
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
29
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
30
+ "onUpdate:modelValue"?: ((value: string | StyleSpecification | undefined) => any) | undefined;
31
+ }>, {
32
+ label: string;
33
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
+ declare const _default: typeof __VLS_export;
35
+ export default _default;
@@ -0,0 +1,51 @@
1
+ import type { ControlPosition } from 'maplibre-gl';
2
+ /** 通用控件容器:把任意 Vue 内容挂到 maplibre 控件槽位,可选折叠为图标按钮。 */
3
+ type __VLS_Props = {
4
+ /** 控件停靠位置;省略用地图默认位置 */
5
+ position?: ControlPosition;
6
+ /**
7
+ * 是否使用 maplibre 控件组样式(白底圆角卡片)
8
+ * @defaultValue true
9
+ */
10
+ group?: boolean;
11
+ /**
12
+ * 是否可折叠:收起时只显示触发按钮,点击展开,面板内关闭按钮或 Esc 收起
13
+ * @defaultValue false
14
+ */
15
+ collapsible?: boolean;
16
+ /**
17
+ * 可折叠时点击控件外部是否收起;常驻展示的内容(如图例)应关闭
18
+ * @defaultValue true
19
+ */
20
+ dismissible?: boolean;
21
+ /** 折叠按钮的无障碍标签 */
22
+ label?: string;
23
+ };
24
+ type __VLS_Slots = {
25
+ /** 控件内容;可折叠时为展开后的面板 */
26
+ default?: () => unknown;
27
+ /** 折叠按钮内容(通常是图标) */
28
+ trigger?: () => unknown;
29
+ };
30
+ type __VLS_ModelProps = {
31
+ /** 可折叠时的展开状态 */
32
+ 'open'?: boolean;
33
+ };
34
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
35
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
36
+ "update:open": (value: boolean) => any;
37
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
38
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
39
+ }>, {
40
+ group: boolean;
41
+ collapsible: boolean;
42
+ dismissible: boolean;
43
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
44
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
45
+ declare const _default: typeof __VLS_export;
46
+ export default _default;
47
+ type __VLS_WithSlots<T, S> = T & {
48
+ new (): {
49
+ $slots: S;
50
+ };
51
+ };