@mapka/maplibre-gl-sdk 0.16.1 → 0.16.3

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 (124) hide show
  1. package/README.md +10 -5
  2. package/lib/.buildInfo.json +1 -1
  3. package/lib/components/ImageCarousel.d.ts +7 -0
  4. package/lib/components/ImageCarousel.d.ts.map +1 -0
  5. package/lib/components/ImageCarousel.js +22 -0
  6. package/lib/components/PopupContent.d.ts +5 -1
  7. package/lib/components/PopupContent.d.ts.map +1 -1
  8. package/lib/components/PopupContent.js +12 -40
  9. package/lib/components/PopupDataRows.d.ts +7 -0
  10. package/lib/components/PopupDataRows.d.ts.map +1 -0
  11. package/lib/components/PopupDataRows.js +22 -0
  12. package/lib/components/PopupList.d.ts +10 -0
  13. package/lib/components/PopupList.d.ts.map +1 -0
  14. package/lib/components/PopupList.js +8 -0
  15. package/lib/components/PopupListItem.d.ts +13 -0
  16. package/lib/components/PopupListItem.d.ts.map +1 -0
  17. package/lib/components/PopupListItem.js +11 -0
  18. package/lib/components/icons/ChevronLeftIcon.d.ts +2 -0
  19. package/lib/components/icons/ChevronLeftIcon.d.ts.map +1 -0
  20. package/lib/components/icons/ChevronLeftIcon.js +4 -0
  21. package/lib/components/icons/ChevronRightIcon.d.ts +2 -0
  22. package/lib/components/icons/ChevronRightIcon.d.ts.map +1 -0
  23. package/lib/components/icons/ChevronRightIcon.js +4 -0
  24. package/lib/components/icons/CircleIcon.d.ts.map +1 -0
  25. package/lib/components/icons/CloseIcon.d.ts +2 -0
  26. package/lib/components/icons/CloseIcon.d.ts.map +1 -0
  27. package/lib/components/icons/CloseIcon.js +4 -0
  28. package/lib/components/icons/DownloadIcon.d.ts.map +1 -0
  29. package/lib/components/icons/FreehandIcon.d.ts.map +1 -0
  30. package/lib/components/icons/HeartIcon.d.ts +4 -0
  31. package/lib/components/icons/HeartIcon.d.ts.map +1 -0
  32. package/lib/components/icons/HeartIcon.js +4 -0
  33. package/lib/components/icons/LineIcon.d.ts.map +1 -0
  34. package/lib/components/icons/PencilIcon.d.ts.map +1 -0
  35. package/lib/components/icons/PolygonIcon.d.ts.map +1 -0
  36. package/lib/components/icons/ProgressDownIcon.d.ts +2 -0
  37. package/lib/components/icons/ProgressDownIcon.d.ts.map +1 -0
  38. package/lib/components/icons/RectangleIcon.d.ts.map +1 -0
  39. package/lib/components/icons/SelectIcon.d.ts.map +1 -0
  40. package/lib/components/icons/TrashIcon.d.ts.map +1 -0
  41. package/lib/controls/MapkaDrawControl.js +7 -7
  42. package/lib/controls/MapkaExportControl.js +2 -2
  43. package/lib/map.d.ts +8 -7
  44. package/lib/map.d.ts.map +1 -1
  45. package/lib/map.js +15 -9
  46. package/lib/modules/layerPopup.d.ts +2 -1
  47. package/lib/modules/layerPopup.d.ts.map +1 -1
  48. package/lib/modules/layerPopup.js +22 -15
  49. package/lib/modules/markers.d.ts +5 -0
  50. package/lib/modules/markers.d.ts.map +1 -1
  51. package/lib/modules/markers.js +21 -16
  52. package/lib/modules/popup.d.ts +3 -10
  53. package/lib/modules/popup.d.ts.map +1 -1
  54. package/lib/modules/popup.js +116 -100
  55. package/lib/modules/popupGroups.d.ts +14 -0
  56. package/lib/modules/popupGroups.d.ts.map +1 -0
  57. package/lib/modules/popupGroups.js +130 -0
  58. package/lib/styles.css +1 -1
  59. package/lib/types/popup.d.ts +10 -1
  60. package/lib/types/popup.d.ts.map +1 -1
  61. package/package.json +26 -7
  62. package/src/components/ImageCarousel.css +73 -0
  63. package/src/components/ImageCarousel.tsx +76 -0
  64. package/src/components/PopupContent.css +52 -189
  65. package/src/components/PopupContent.tsx +26 -195
  66. package/src/components/PopupDataRows.css +41 -0
  67. package/src/components/PopupDataRows.tsx +39 -0
  68. package/src/components/PopupList.css +24 -0
  69. package/src/components/PopupList.tsx +27 -0
  70. package/src/components/PopupListItem.css +61 -0
  71. package/src/components/PopupListItem.tsx +40 -0
  72. package/src/components/icons/ChevronLeftIcon.tsx +20 -0
  73. package/src/components/icons/ChevronRightIcon.tsx +20 -0
  74. package/src/components/icons/CloseIcon.tsx +13 -0
  75. package/src/components/icons/HeartIcon.tsx +18 -0
  76. package/src/components/{ProgressDownIcon.tsx → icons/ProgressDownIcon.tsx} +0 -3
  77. package/src/controls/MapkaDrawControl.tsx +7 -7
  78. package/src/controls/MapkaExportControl.tsx +2 -2
  79. package/src/map.ts +22 -20
  80. package/src/modules/layerPopup.ts +32 -21
  81. package/src/modules/markers.ts +26 -16
  82. package/src/modules/popup.tsx +129 -112
  83. package/src/modules/popupGroups.ts +190 -0
  84. package/src/styles.css +4 -0
  85. package/src/types/popup.ts +12 -1
  86. package/lib/components/CircleIcon.d.ts.map +0 -1
  87. package/lib/components/DownloadIcon.d.ts.map +0 -1
  88. package/lib/components/FreehandIcon.d.ts.map +0 -1
  89. package/lib/components/LineIcon.d.ts.map +0 -1
  90. package/lib/components/PencilIcon.d.ts.map +0 -1
  91. package/lib/components/PolygonIcon.d.ts.map +0 -1
  92. package/lib/components/ProgressDownIcon.d.ts +0 -4
  93. package/lib/components/ProgressDownIcon.d.ts.map +0 -1
  94. package/lib/components/RectangleIcon.d.ts.map +0 -1
  95. package/lib/components/SelectIcon.d.ts.map +0 -1
  96. package/lib/components/TrashIcon.d.ts.map +0 -1
  97. /package/lib/components/{CircleIcon.d.ts → icons/CircleIcon.d.ts} +0 -0
  98. /package/lib/components/{CircleIcon.js → icons/CircleIcon.js} +0 -0
  99. /package/lib/components/{DownloadIcon.d.ts → icons/DownloadIcon.d.ts} +0 -0
  100. /package/lib/components/{DownloadIcon.js → icons/DownloadIcon.js} +0 -0
  101. /package/lib/components/{FreehandIcon.d.ts → icons/FreehandIcon.d.ts} +0 -0
  102. /package/lib/components/{FreehandIcon.js → icons/FreehandIcon.js} +0 -0
  103. /package/lib/components/{LineIcon.d.ts → icons/LineIcon.d.ts} +0 -0
  104. /package/lib/components/{LineIcon.js → icons/LineIcon.js} +0 -0
  105. /package/lib/components/{PencilIcon.d.ts → icons/PencilIcon.d.ts} +0 -0
  106. /package/lib/components/{PencilIcon.js → icons/PencilIcon.js} +0 -0
  107. /package/lib/components/{PolygonIcon.d.ts → icons/PolygonIcon.d.ts} +0 -0
  108. /package/lib/components/{PolygonIcon.js → icons/PolygonIcon.js} +0 -0
  109. /package/lib/components/{ProgressDownIcon.js → icons/ProgressDownIcon.js} +0 -0
  110. /package/lib/components/{RectangleIcon.d.ts → icons/RectangleIcon.d.ts} +0 -0
  111. /package/lib/components/{RectangleIcon.js → icons/RectangleIcon.js} +0 -0
  112. /package/lib/components/{SelectIcon.d.ts → icons/SelectIcon.d.ts} +0 -0
  113. /package/lib/components/{SelectIcon.js → icons/SelectIcon.js} +0 -0
  114. /package/lib/components/{TrashIcon.d.ts → icons/TrashIcon.d.ts} +0 -0
  115. /package/lib/components/{TrashIcon.js → icons/TrashIcon.js} +0 -0
  116. /package/src/components/{CircleIcon.tsx → icons/CircleIcon.tsx} +0 -0
  117. /package/src/components/{DownloadIcon.tsx → icons/DownloadIcon.tsx} +0 -0
  118. /package/src/components/{FreehandIcon.tsx → icons/FreehandIcon.tsx} +0 -0
  119. /package/src/components/{LineIcon.tsx → icons/LineIcon.tsx} +0 -0
  120. /package/src/components/{PencilIcon.tsx → icons/PencilIcon.tsx} +0 -0
  121. /package/src/components/{PolygonIcon.tsx → icons/PolygonIcon.tsx} +0 -0
  122. /package/src/components/{RectangleIcon.tsx → icons/RectangleIcon.tsx} +0 -0
  123. /package/src/components/{SelectIcon.tsx → icons/SelectIcon.tsx} +0 -0
  124. /package/src/components/{TrashIcon.tsx → icons/TrashIcon.tsx} +0 -0
@@ -0,0 +1,61 @@
1
+ .mapka-popup-list-item {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 4px;
5
+ background: #fff;
6
+ border-radius: 8px;
7
+ overflow: hidden;
8
+ cursor: pointer;
9
+ }
10
+
11
+ .mapka-popup-list-item + .mapka-popup-list-item {
12
+ border-top: 1px solid #dcdfe3;
13
+ padding-top: 8px;
14
+ }
15
+
16
+ .mapka-popup-list-item-header {
17
+ display: flex;
18
+ flex-direction: row;
19
+ height: 90px;
20
+ overflow: hidden;
21
+ border-radius: 8px;
22
+ }
23
+
24
+ .mapka-popup-list-item-image {
25
+ width: 90px;
26
+ height: 90px;
27
+ border-radius: 8px;
28
+ object-fit: cover;
29
+ flex-shrink: 0;
30
+ background: #ccd1d6;
31
+ }
32
+
33
+ .mapka-popup-list-item-info {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: 4px;
37
+ flex: 1;
38
+ padding: 4px 4px 4px 8px;
39
+ min-width: 0;
40
+ }
41
+
42
+ .mapka-popup-list-item-title {
43
+ margin: 0;
44
+ font-weight: 700;
45
+ font-size: 16px;
46
+ line-height: 20px;
47
+ color: #4f4f4f;
48
+ }
49
+
50
+ .mapka-popup-list-item-description {
51
+ margin: 0;
52
+ font-weight: 400;
53
+ font-size: 12px;
54
+ line-height: 16px;
55
+ color: #4f4f4f;
56
+ overflow: hidden;
57
+ display: -webkit-box;
58
+ -webkit-line-clamp: 3;
59
+ line-clamp: 3;
60
+ -webkit-box-orient: vertical;
61
+ }
@@ -0,0 +1,40 @@
1
+ import { PopupDataRows } from "./PopupDataRows.js";
2
+ import type { MapkaPopupContent } from "../types/popup.js";
3
+
4
+ interface PopupHeaderProps {
5
+ title?: string;
6
+ description?: string;
7
+ imageUrls: string[];
8
+ }
9
+
10
+ export function PopupHeader({ title, description, imageUrls: [firstImage] }: PopupHeaderProps) {
11
+ return (
12
+ <div class="mapka-popup-list-item-header">
13
+ {firstImage && <img src={firstImage} alt={title} class="mapka-popup-list-item-image" />}
14
+
15
+ <div class="mapka-popup-list-item-info">
16
+ {title && <span class="mapka-popup-list-item-title">{title}</span>}
17
+ {description && <span class="mapka-popup-list-item-description">{description}</span>}
18
+ </div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+ interface PopupCollectionItemProps {
24
+ popup: MapkaPopupContent;
25
+ }
26
+
27
+ export function PopupListItem({
28
+ popup: { title, description, rows = [], imageUrls = [] },
29
+ }: PopupCollectionItemProps) {
30
+ const hasImages = Boolean(imageUrls.length);
31
+ const hasHeader = Boolean(title || description || hasImages);
32
+ const hasDataRows = Boolean(rows.length);
33
+
34
+ return (
35
+ <div class="mapka-popup-list-item">
36
+ {hasHeader && <PopupHeader title={title} description={description} imageUrls={imageUrls} />}
37
+ {hasDataRows && <PopupDataRows rows={rows} />}
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,20 @@
1
+ export function ChevronLeftIcon() {
2
+ return (
3
+ <svg
4
+ viewBox="0 0 16 16"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ aria-hidden="true"
7
+ focusable="false"
8
+ class="mapka-popup-icon mapka-popup-icon-sm"
9
+ >
10
+ <path
11
+ d="M10 3 5.7 7.3a1 1 0 0 0 0 1.4L10 13"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ stroke-width="1.5"
15
+ stroke-linecap="round"
16
+ stroke-linejoin="round"
17
+ />
18
+ </svg>
19
+ );
20
+ }
@@ -0,0 +1,20 @@
1
+ export function ChevronRightIcon() {
2
+ return (
3
+ <svg
4
+ viewBox="0 0 16 16"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ aria-hidden="true"
7
+ focusable="false"
8
+ class="mapka-popup-icon mapka-popup-icon-sm"
9
+ >
10
+ <path
11
+ d="m6 3 4.3 4.3a1 1 0 0 1 0 1.4L6 13"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ stroke-width="1.5"
15
+ stroke-linecap="round"
16
+ stroke-linejoin="round"
17
+ />
18
+ </svg>
19
+ );
20
+ }
@@ -0,0 +1,13 @@
1
+ export function CloseIcon() {
2
+ return (
3
+ <svg
4
+ viewBox="0 0 32 32"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ aria-hidden="true"
7
+ focusable="false"
8
+ class="mapka-popup-icon"
9
+ >
10
+ <path d="m8 8 16 16M24 8 8 24" fill="none" stroke="currentColor" stroke-width="2" />
11
+ </svg>
12
+ );
13
+ }
@@ -0,0 +1,18 @@
1
+ export function HeartIcon({ filled }: { filled?: boolean }) {
2
+ return (
3
+ <svg
4
+ viewBox="0 0 32 32"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ aria-hidden="true"
7
+ focusable="false"
8
+ class="mapka-popup-icon"
9
+ >
10
+ <path
11
+ d="M16 28c7-4.73 14-10 14-17a6.98 6.98 0 0 0-7-7c-1.8 0-3.58.68-4.95 2.05L16 8.1l-2.05-2.05a6.98 6.98 0 0 0-9.9 0A6.98 6.98 0 0 0 2 11c0 7 7 12.27 14 17z"
12
+ fill={filled ? "currentColor" : "none"}
13
+ stroke="currentColor"
14
+ stroke-width="2"
15
+ />
16
+ </svg>
17
+ );
18
+ }
@@ -1,6 +1,3 @@
1
- /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
- import { h } from "preact";
3
-
4
1
  export const ProgressDownIcon = () => {
5
2
  return (
6
3
  <svg
@@ -10,13 +10,13 @@ import {
10
10
  TerraDrawFreehandMode,
11
11
  } from "terra-draw";
12
12
  import { TerraDrawMapLibreGLAdapter } from "terra-draw-maplibre-gl-adapter";
13
- import { SelectIcon } from "../components/SelectIcon.js";
14
- import { PolygonIcon } from "../components/PolygonIcon.js";
15
- import { RectangleIcon } from "../components/RectangleIcon.js";
16
- import { CircleIcon } from "../components/CircleIcon.js";
17
- import { LineIcon } from "../components/LineIcon.js";
18
- import { TrashIcon } from "../components/TrashIcon.js";
19
- import { FreehandIcon } from "../components/FreehandIcon.js";
13
+ import { SelectIcon } from "../components/icons/SelectIcon.js";
14
+ import { PolygonIcon } from "../components/icons/PolygonIcon.js";
15
+ import { RectangleIcon } from "../components/icons/RectangleIcon.js";
16
+ import { CircleIcon } from "../components/icons/CircleIcon.js";
17
+ import { LineIcon } from "../components/icons/LineIcon.js";
18
+ import { TrashIcon } from "../components/icons/TrashIcon.js";
19
+ import { FreehandIcon } from "../components/icons/FreehandIcon.js";
20
20
  import { isEmpty } from "es-toolkit/compat";
21
21
  import type { IControl } from "maplibre-gl";
22
22
  import type { GeoJSONStoreFeatures } from "terra-draw";
@@ -1,6 +1,6 @@
1
1
  import { render } from "preact";
2
- import { DownloadIcon } from "../components/DownloadIcon.js";
3
- import { ProgressDownIcon } from "../components/ProgressDownIcon.js";
2
+ import { DownloadIcon } from "../components/icons/DownloadIcon.js";
3
+ import { ProgressDownIcon } from "../components/icons/ProgressDownIcon.js";
4
4
  import type { IControl } from "maplibre-gl";
5
5
  import type { MapkaMap } from "../map.js";
6
6
  import type { MapkaExportOptions } from "../types/export.js";
package/src/map.ts CHANGED
@@ -1,13 +1,7 @@
1
1
  import * as maplibregl from "maplibre-gl";
2
2
  import { loadLayersIcons } from "./modules/icons.js";
3
3
  import { exportMap } from "./modules/export.js";
4
- import {
5
- closeOnMapClickPopups,
6
- closePopupsById,
7
- openPopup,
8
- removePopups,
9
- updatePopup,
10
- } from "./modules/popup.js";
4
+ import { closePopupsByIds, reconciliatePopups, closePopups } from "./modules/popup.js";
11
5
  import {
12
6
  addMarkers,
13
7
  addStyleDiffMarkers,
@@ -28,9 +22,9 @@ import type {
28
22
  } from "maplibre-gl";
29
23
  import type { MapkaMarkerOptions } from "./types/marker.js";
30
24
  import type { MapkaExportOptions } from "./types/export.js";
31
- import type { MapkaPopupOptions } from "./types/popup.js";
25
+ import type { MapkaPopupOptions, MapkaPopupOptionsResolved } from "./types/popup.js";
32
26
  import type { MapkaAddLayerObject } from "./types/layer.js";
33
- import { openOnFeatureClickPopups } from "./modules/layerPopup.js";
27
+ import { openClickPopups } from "./modules/layerPopup.js";
34
28
 
35
29
  export interface MapkaMapOptions extends MapOptions {
36
30
  maxPopups?: number;
@@ -65,8 +59,8 @@ const noopTransformStyle: TransformStyleFunction = (_, next) => {
65
59
  };
66
60
 
67
61
  export type MapMapkaPopup = {
68
- id: string | string[];
69
- options: MapkaPopupOptions;
62
+ ids: string[];
63
+ options: MapkaPopupOptionsResolved[];
70
64
  container: HTMLElement;
71
65
  popup: Popup;
72
66
  };
@@ -93,21 +87,25 @@ export class MapkaMap extends maplibregl.Map {
93
87
  public scrollPopups: boolean = true;
94
88
  public popups: MapMapkaPopup[] = [];
95
89
 
96
- public constructor({ transformRequest, apiKey, maxPopups = 1, ...options }: MapkaMapOptions) {
90
+ public constructor({ transformRequest, apiKey, maxPopups = 1, scrollPopups = true, ...options }: MapkaMapOptions) {
97
91
  super({
98
92
  ...options,
99
93
  transformRequest: createTransformRequest(apiKey, transformRequest),
100
94
  });
101
95
 
102
96
  this.maxPopups = maxPopups;
97
+ this.scrollPopups = scrollPopups;
103
98
 
104
99
  super.on("styleimagemissing", (event: MapStyleImageMissingEvent) => {
105
100
  loadLayersIcons(this, event);
106
101
  });
107
102
 
108
103
  super.on("click", (event: maplibregl.MapMouseEvent) => {
109
- closeOnMapClickPopups(this);
110
- openOnFeatureClickPopups(this, event);
104
+ openClickPopups(this, event);
105
+ });
106
+
107
+ super.on("zoomend", () => {
108
+ reconciliatePopups(this);
111
109
  });
112
110
 
113
111
  super.on("style.load", () => {
@@ -144,20 +142,24 @@ export class MapkaMap extends maplibregl.Map {
144
142
  removeMarkers(this);
145
143
  }
146
144
 
147
- public openPopup(popup: MapkaPopupOptions) {
148
- return openPopup(this, popup);
145
+ public openPopup(popup: MapkaPopupOptions | MapkaPopupOptions[]) {
146
+ return reconciliatePopups(this, Array.isArray(popup) ? popup : [popup]);
149
147
  }
150
148
 
151
- public closePopup(id: string) {
152
- closePopupsById(this, id);
149
+ public closePopup(id: string | string[]) {
150
+ closePopupsByIds(this, Array.isArray(id) ? id : [id]);
153
151
  }
154
152
 
155
153
  public updatePopup(popup: MapkaPopupOptions) {
156
- return updatePopup(this, popup);
154
+ return reconciliatePopups(this, Array.isArray(popup) ? popup : [popup]);
155
+ }
156
+
157
+ public getPopups() {
158
+ return this.popups;
157
159
  }
158
160
 
159
161
  public removePopups() {
160
- removePopups(this);
162
+ closePopups(this);
161
163
  }
162
164
 
163
165
  public async export(options?: MapkaExportOptions) {
@@ -1,33 +1,16 @@
1
1
  import { get } from "es-toolkit/compat";
2
2
  import { isBoolean, isPlainObject } from "es-toolkit";
3
- import type { MapMouseEvent, MapGeoJSONFeature, LngLat } from "maplibre-gl";
3
+ import { closeOnMapClickPopups, getPopupId } from "./popup.js";
4
+ import type { MapMouseEvent, MapGeoJSONFeature, LngLat, Point } from "maplibre-gl";
4
5
  import type { MapkaMap } from "../map.js";
5
6
  import type { MapkaLayerPopupOptions } from "../types/popup.js";
6
7
  import type { MapkaPopupOptions } from "../types/popup.js";
7
8
 
8
- export function openOnFeatureClickPopups(map: MapkaMap, { lngLat, point }: MapMouseEvent) {
9
- const features = map.queryRenderedFeatures(point);
10
-
11
- if (features.length === 0) {
12
- return;
13
- }
14
-
15
- const popups = getFeaturePopups(features, lngLat);
16
-
17
- if (popups.length === 0) {
18
- return;
19
- }
20
-
21
- for (const popup of popups) {
22
- map.openPopup(popup);
23
- }
24
- }
25
-
26
9
  function genericPropertiesToPopup(feature: MapGeoJSONFeature, lngLat: LngLat): MapkaPopupOptions {
27
10
  const { id, properties, layer } = feature;
28
11
 
29
12
  return {
30
- id: id ?? properties.id,
13
+ id: id ?? properties.id ?? getPopupId(layer),
31
14
  lngLat: lngLat.toArray(),
32
15
  content: {
33
16
  title: layer.id,
@@ -46,7 +29,13 @@ function buildConfigDrivenPopup(
46
29
  throw new Error("Not implemented", { cause: { config, feature, lngLat } });
47
30
  }
48
31
 
49
- function getFeaturePopups(features: MapGeoJSONFeature[], lngLat: LngLat): MapkaPopupOptions[] {
32
+ function getFeaturePopups(
33
+ map: MapkaMap,
34
+ point: Point,
35
+ lngLat: LngLat,
36
+ trigger: "click" | "hover",
37
+ ): MapkaPopupOptions[] {
38
+ const features = map.queryRenderedFeatures(point);
50
39
  const popups: MapkaPopupOptions[] = [];
51
40
 
52
41
  for (const feature of features) {
@@ -66,9 +55,31 @@ function getFeaturePopups(features: MapGeoJSONFeature[], lngLat: LngLat): MapkaP
66
55
  }
67
56
 
68
57
  if (isPlainObject(mapkaPopup)) {
58
+ if (mapkaPopup.trigger && mapkaPopup.trigger !== trigger) {
59
+ continue;
60
+ }
69
61
  popups.push(buildConfigDrivenPopup(feature, mapkaPopup, lngLat));
70
62
  }
71
63
  }
72
64
 
73
65
  return popups;
74
66
  }
67
+
68
+ export function openClickPopups(map: MapkaMap, { lngLat, point }: MapMouseEvent) {
69
+ closeOnMapClickPopups(map);
70
+
71
+ const featurePopups = getFeaturePopups(map, point, lngLat, "click");
72
+ if (featurePopups.length === 0) {
73
+ return;
74
+ }
75
+ map.openPopup(featurePopups);
76
+ }
77
+
78
+ export function openOnHoverPopups(map: MapkaMap, { lngLat, point }: MapMouseEvent) {
79
+ const featurePopups = getFeaturePopups(map, point, lngLat, "hover");
80
+
81
+ if (featurePopups.length === 0) {
82
+ return;
83
+ }
84
+ map.openPopup(featurePopups);
85
+ }
@@ -1,6 +1,5 @@
1
1
  import { Marker } from "maplibre-gl";
2
2
  import { get } from "es-toolkit/compat";
3
- import { getPopupId } from "./popup.js";
4
3
  import { remove } from "es-toolkit";
5
4
  import type { Offset, StyleSpecification } from "maplibre-gl";
6
5
  import type { MapkaMap } from "../map.js";
@@ -26,17 +25,27 @@ export function getMarkerId(marker: { id?: string }) {
26
25
  return marker.id ?? `marker-${crypto.randomUUID()}`;
27
26
  }
28
27
 
29
- const markerPopupOptions = (marker: Marker, popupOptions: Omit<MapkaPopupOptions, "lngLat">) => {
28
+ export function getMarkerPopupId(marker: { id?: string }, popup: { id?: string }) {
29
+ return popup.id ?? marker.id ?? `marker-popup-${crypto.randomUUID()}`;
30
+ }
31
+
32
+ const markerPopupOptions = (
33
+ id: string,
34
+ marker: Marker,
35
+ popupOptions: Omit<MapkaPopupOptions, "lngLat">,
36
+ ) => {
30
37
  const latLng = marker.getLngLat().toArray();
31
38
 
32
39
  if ("offset" in popupOptions) {
33
40
  return {
34
41
  ...popupOptions,
42
+ id,
35
43
  lngLat: latLng,
36
44
  };
37
45
  } else {
38
46
  return {
39
47
  ...popupOptions,
48
+ id,
40
49
  lngLat: latLng,
41
50
  offset: DEFAULT_MARKET_OFFSET,
42
51
  };
@@ -49,51 +58,52 @@ function setupMarkerPopupListeners(
49
58
  popup: MapkaMarkerPopupOptions,
50
59
  options: MapkaMarkerOptions,
51
60
  ) {
52
- const popupId = getPopupId(popup);
53
61
  const markerElement = marker.getElement();
62
+ const popupId = getMarkerPopupId(options, popup);
63
+ const popupOptions = markerPopupOptions(popupId, marker, popup);
54
64
 
55
65
  if (options.draggable) {
56
66
  marker.on("dragend", () => {
57
- if (map.popups.find((p) => p.id === popupId)) {
58
- map.updatePopup(markerPopupOptions(marker, popup));
67
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
68
+ map.updatePopup(popupOptions);
59
69
  }
60
70
  });
61
71
  marker.on("drag", () => {
62
- if (map.popups.find((p) => p.id === popupId)) {
63
- map.updatePopup(markerPopupOptions(marker, popup));
72
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
73
+ map.updatePopup(popupOptions);
64
74
  }
65
75
  });
66
76
  }
67
77
 
68
78
  if (popup.trigger === "always") {
69
- if (!map.popups.find((p) => p.id === popupId)) {
70
- map.openPopup(markerPopupOptions(marker, popup));
79
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
80
+ map.openPopup(popupOptions);
71
81
  }
72
82
 
73
83
  markerElement.addEventListener("click", (e) => {
74
84
  e.stopPropagation();
75
- if (!map.popups.find((p) => p.id === popupId)) {
76
- map.openPopup(markerPopupOptions(marker, popup));
85
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
86
+ map.openPopup(popupOptions);
77
87
  }
78
88
  });
79
89
  } else if (popup.trigger === "click") {
80
90
  markerElement.style.cursor = "pointer";
81
91
  markerElement.addEventListener("click", (e) => {
82
92
  e.stopPropagation();
83
- if (!map.popups.find((p) => p.id === popupId)) {
84
- map.openPopup(markerPopupOptions(marker, popup));
93
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
94
+ map.openPopup(popupOptions);
85
95
  }
86
96
  });
87
97
  } else if (popup.trigger === "hover") {
88
98
  markerElement.addEventListener("mouseenter", (e) => {
89
99
  e.stopPropagation();
90
- if (!map.popups.find((p) => p.id === popupId)) {
91
- map.openPopup(markerPopupOptions(marker, popup));
100
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
101
+ map.openPopup(popupOptions);
92
102
  }
93
103
  });
94
104
  markerElement.addEventListener("mouseleave", (e) => {
95
105
  e.stopPropagation();
96
- if (map.popups.find((p) => p.id === popupId)) {
106
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
97
107
  map.closePopup(popupId);
98
108
  }
99
109
  });