@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
@@ -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
  /**
6
5
  * Default marker offset
@@ -19,45 +18,51 @@ const DEFAULT_MARKET_OFFSET = {
19
18
  export function getMarkerId(marker) {
20
19
  return marker.id ?? `marker-${crypto.randomUUID()}`;
21
20
  }
22
- const markerPopupOptions = (marker, popupOptions) => {
21
+ export function getMarkerPopupId(marker, popup) {
22
+ return popup.id ?? marker.id ?? `marker-popup-${crypto.randomUUID()}`;
23
+ }
24
+ const markerPopupOptions = (id, marker, popupOptions) => {
23
25
  const latLng = marker.getLngLat().toArray();
24
26
  if ("offset" in popupOptions) {
25
27
  return {
26
28
  ...popupOptions,
29
+ id,
27
30
  lngLat: latLng,
28
31
  };
29
32
  }
30
33
  else {
31
34
  return {
32
35
  ...popupOptions,
36
+ id,
33
37
  lngLat: latLng,
34
38
  offset: DEFAULT_MARKET_OFFSET,
35
39
  };
36
40
  }
37
41
  };
38
42
  function setupMarkerPopupListeners(map, marker, popup, options) {
39
- const popupId = getPopupId(popup);
40
43
  const markerElement = marker.getElement();
44
+ const popupId = getMarkerPopupId(options, popup);
45
+ const popupOptions = markerPopupOptions(popupId, marker, popup);
41
46
  if (options.draggable) {
42
47
  marker.on("dragend", () => {
43
- if (map.popups.find((p) => p.id === popupId)) {
44
- map.updatePopup(markerPopupOptions(marker, popup));
48
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
49
+ map.updatePopup(popupOptions);
45
50
  }
46
51
  });
47
52
  marker.on("drag", () => {
48
- if (map.popups.find((p) => p.id === popupId)) {
49
- map.updatePopup(markerPopupOptions(marker, popup));
53
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
54
+ map.updatePopup(popupOptions);
50
55
  }
51
56
  });
52
57
  }
53
58
  if (popup.trigger === "always") {
54
- if (!map.popups.find((p) => p.id === popupId)) {
55
- map.openPopup(markerPopupOptions(marker, popup));
59
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
60
+ map.openPopup(popupOptions);
56
61
  }
57
62
  markerElement.addEventListener("click", (e) => {
58
63
  e.stopPropagation();
59
- if (!map.popups.find((p) => p.id === popupId)) {
60
- map.openPopup(markerPopupOptions(marker, popup));
64
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
65
+ map.openPopup(popupOptions);
61
66
  }
62
67
  });
63
68
  }
@@ -65,21 +70,21 @@ function setupMarkerPopupListeners(map, marker, popup, options) {
65
70
  markerElement.style.cursor = "pointer";
66
71
  markerElement.addEventListener("click", (e) => {
67
72
  e.stopPropagation();
68
- if (!map.popups.find((p) => p.id === popupId)) {
69
- map.openPopup(markerPopupOptions(marker, popup));
73
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
74
+ map.openPopup(popupOptions);
70
75
  }
71
76
  });
72
77
  }
73
78
  else if (popup.trigger === "hover") {
74
79
  markerElement.addEventListener("mouseenter", (e) => {
75
80
  e.stopPropagation();
76
- if (!map.popups.find((p) => p.id === popupId)) {
77
- map.openPopup(markerPopupOptions(marker, popup));
81
+ if (!map.popups.find((p) => p.ids.includes(popupId))) {
82
+ map.openPopup(popupOptions);
78
83
  }
79
84
  });
80
85
  markerElement.addEventListener("mouseleave", (e) => {
81
86
  e.stopPropagation();
82
- if (map.popups.find((p) => p.id === popupId)) {
87
+ if (map.popups.find((p) => p.ids.includes(popupId))) {
83
88
  map.closePopup(popupId);
84
89
  }
85
90
  });
@@ -1,18 +1,11 @@
1
- import { Popup } from "maplibre-gl";
2
1
  import type { MapkaPopupOptions } from "../types/popup.js";
3
2
  import type { MapkaMap } from "../map.js";
4
3
  export declare function getPopupId(popup: {
5
4
  id?: string;
6
5
  }): string;
7
- export declare function getOnClose(map: MapkaMap, id: string): () => void;
8
6
  export declare function enforceMaxPopups(map: MapkaMap): void;
9
- export declare function openPopup(map: MapkaMap, options: MapkaPopupOptions): string;
10
- export declare function updatePopupBaseOptions(popup: Popup, options: MapkaPopupOptions, newOptions: Omit<MapkaPopupOptions, "content">): Popup;
11
- export declare function updatePopup(map: MapkaMap, { content, ...newOptions }: MapkaPopupOptions): void;
12
- /**
13
- * Close all popups that have closeOnClick set to true or undefined
14
- */
7
+ export declare function reconciliatePopups(map: MapkaMap, options?: MapkaPopupOptions[]): string[];
15
8
  export declare function closeOnMapClickPopups(map: MapkaMap): void;
16
- export declare function closePopupsById(map: MapkaMap, id: string): void;
17
- export declare function removePopups(map: MapkaMap): void;
9
+ export declare function closePopups(map: MapkaMap): void;
10
+ export declare function closePopupsByIds(map: MapkaMap, ids: string[]): void;
18
11
  //# sourceMappingURL=popup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"popup.d.ts","sourceRoot":"","sources":["../../src/modules/popup.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAEhD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,cAEnD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,QAS7C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,UAqDlE;AAID,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,SAY/C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,EAAE,iBAAiB,QA0BvF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,QAYlD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QASxD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,QASzC"}
1
+ {"version":3,"file":"popup.d.ts","sourceRoot":"","sources":["../../src/modules/popup.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAA6B,MAAM,mBAAmB,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,WAAW,CAAC;AAEzD,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAEhD;AAmFD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,QAW7C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAiB,EAAO,YAelF;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,QAclD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,QASxC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QA+B5D"}
@@ -1,146 +1,162 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
2
  import { Popup } from "maplibre-gl";
3
3
  import { PopupContent } from "../components/PopupContent.js";
4
+ import { PopupList } from "../components/PopupList.js";
4
5
  import { render } from "preact";
5
6
  import { remove } from "es-toolkit/array";
6
- import { isEqual, isPlainObject } from "es-toolkit";
7
+ import { isPlainObject, without } from "es-toolkit";
8
+ import { computePopupGroups } from "./popupGroups.js";
7
9
  export function getPopupId(popup) {
8
10
  return popup.id ?? `popup-${crypto.randomUUID()}`;
9
11
  }
10
- export function getOnClose(map, id) {
11
- return () => map.closePopup(id);
12
+ function getOnClose(map, id) {
13
+ return () => closePopupsByIds(map, [id]);
12
14
  }
13
- export function enforceMaxPopups(map) {
14
- if (map.popups.length > map.maxPopups) {
15
- const popupToRemove = map.popups.shift();
16
- popupToRemove?.popup.remove();
17
- if (isPlainObject(popupToRemove?.options.content)) {
18
- render(null, popupToRemove.container);
15
+ function hasObjectContent(options) {
16
+ return options.some((opt) => isPlainObject(opt.content));
17
+ }
18
+ function resolveContentCreators(options) {
19
+ return options.map((opt) => {
20
+ const id = getPopupId(opt);
21
+ if (typeof opt.content === "function") {
22
+ return {
23
+ ...opt,
24
+ id,
25
+ content: opt.content(id),
26
+ };
19
27
  }
20
- popupToRemove?.container.remove();
21
- }
28
+ else {
29
+ return {
30
+ ...opt,
31
+ id,
32
+ content: opt.content,
33
+ };
34
+ }
35
+ });
22
36
  }
23
- export function openPopup(map, options) {
24
- const { lngLat, content, closeButton, id = getPopupId(options), ...popupOptions } = options;
25
- if (content instanceof HTMLElement) {
26
- const popup = new Popup({
27
- ...popupOptions,
28
- closeButton: false,
29
- closeOnClick: false,
30
- })
31
- .setLngLat(lngLat)
32
- .setDOMContent(content)
33
- .addTo(map);
34
- map.popups.push({
35
- container: content,
36
- id,
37
- options,
38
- popup,
39
- });
40
- enforceMaxPopups(map);
41
- return id;
42
- }
43
- else if (typeof content === "object") {
44
- const onClose = getOnClose(map, id);
45
- const container = document.createElement("div");
46
- container.classList.add("mapka-popup-container");
47
- render(_jsx(PopupContent, { ...content, closeButton: closeButton, onClose: onClose }), container);
48
- const popup = new Popup({
49
- ...popupOptions,
37
+ function createNewPopup(map, options) {
38
+ const [{ lngLat, id, content, closeButton, ...opts }] = options;
39
+ const ids = options.map(getPopupId);
40
+ const container = document.createElement("div");
41
+ container.classList.add("mapka-popup-container");
42
+ let popup;
43
+ if (options.length > 1) {
44
+ render(_jsx(PopupList, { items: options }), container);
45
+ popup = new Popup({
46
+ ...opts,
50
47
  closeButton: false,
51
- closeOnClick: false,
48
+ closeOnClick: true,
52
49
  })
53
50
  .setLngLat(lngLat)
54
51
  .setDOMContent(container)
55
52
  .addTo(map);
56
- map.popups.push({
57
- container,
58
- id,
59
- options,
60
- popup,
61
- });
62
- enforceMaxPopups(map);
63
- return id;
64
53
  }
65
- else if (typeof content === "function") {
66
- const newContent = content(id);
67
- return openPopup(map, {
68
- ...options,
69
- content: newContent,
70
- });
54
+ else {
55
+ if (content instanceof HTMLElement) {
56
+ popup = new Popup({
57
+ ...opts,
58
+ closeButton: false,
59
+ })
60
+ .setLngLat(lngLat)
61
+ .setDOMContent(content)
62
+ .addTo(map);
63
+ }
64
+ else {
65
+ popup = new Popup({
66
+ ...opts,
67
+ closeButton: false,
68
+ })
69
+ .setLngLat(lngLat)
70
+ .setDOMContent(container)
71
+ .addTo(map);
72
+ render(_jsx(PopupContent, { ...content, onClose: getOnClose(map, id) }), container);
73
+ }
71
74
  }
72
- throw new Error("Invalid popup content");
75
+ if (!popup)
76
+ return;
77
+ map.popups.push({
78
+ container,
79
+ ids,
80
+ options,
81
+ popup,
82
+ });
83
+ return ids;
73
84
  }
74
- const DEFAULT_POPUP_MAX_WIDTH = "240px";
75
- export function updatePopupBaseOptions(popup, options, newOptions) {
76
- if (!isEqual(options.maxWidth, newOptions.maxWidth)) {
77
- popup.setMaxWidth(newOptions.maxWidth ?? DEFAULT_POPUP_MAX_WIDTH);
78
- }
79
- if (!isEqual(options.offset, newOptions.offset)) {
80
- popup.setOffset(newOptions.offset);
81
- }
82
- if (!isEqual(options.lngLat, newOptions.lngLat)) {
83
- popup.setLngLat(newOptions.lngLat);
85
+ export function enforceMaxPopups(map) {
86
+ if (map.popups.length > map.maxPopups) {
87
+ const popupToRemove = map.popups.shift();
88
+ if (popupToRemove) {
89
+ popupToRemove.popup.remove();
90
+ if (hasObjectContent(popupToRemove.options)) {
91
+ render(null, popupToRemove.container);
92
+ }
93
+ popupToRemove.container.remove();
94
+ }
84
95
  }
85
- return popup;
86
96
  }
87
- export function updatePopup(map, { content, ...newOptions }) {
88
- const id = getPopupId(newOptions);
89
- if (content instanceof HTMLElement) {
90
- const mapkaPopups = map.popups.filter((popup) => popup.id === id);
91
- for (const { popup, options } of mapkaPopups) {
92
- updatePopupBaseOptions(popup, options, newOptions);
93
- popup.setDOMContent(content);
97
+ export function reconciliatePopups(map, options = []) {
98
+ const resolved = resolveContentCreators(options);
99
+ const actions = computePopupGroups(map, resolved);
100
+ for (const action of actions) {
101
+ if (action.type === "close") {
102
+ closePopupsByIds(map, action.ids);
94
103
  }
95
- }
96
- else if (typeof content === "object") {
97
- const onClose = getOnClose(map, id);
98
- const mapkaPopups = map.popups.filter((popup) => popup.id === id);
99
- for (const { popup, container, options } of mapkaPopups) {
100
- const { closeButton } = options;
101
- render(_jsx(PopupContent, { ...content, closeButton: closeButton, onClose: onClose }), container);
102
- updatePopupBaseOptions(popup, options, newOptions);
103
- popup.setDOMContent(container);
104
+ else if (action.type === "create") {
105
+ createNewPopup(map, action.options);
104
106
  }
105
107
  }
106
- else if (typeof content === "function") {
107
- const newContent = content(id);
108
- return updatePopup(map, {
109
- ...newOptions,
110
- content: newContent,
111
- });
112
- }
108
+ enforceMaxPopups(map);
109
+ return resolved.map((opt) => opt.id);
113
110
  }
114
- /**
115
- * Close all popups that have closeOnClick set to true or undefined
116
- */
117
111
  export function closeOnMapClickPopups(map) {
118
- const popupsToCloseOnMapClick = remove(map.popups, (popup) => popup.options.closeOnClick === true || popup.options.closeOnClick === undefined);
112
+ const popupsToCloseOnMapClick = remove(map.popups, (popup) => {
113
+ const [first] = popup.options;
114
+ return (popup.ids.length > 1 || first?.closeOnClick === true || first?.closeOnClick === undefined);
115
+ });
119
116
  for (const popup of popupsToCloseOnMapClick) {
120
117
  popup.popup.remove();
121
- if (isPlainObject(popup.options.content)) {
118
+ if (hasObjectContent(popup.options)) {
122
119
  render(null, popup.container);
123
120
  }
124
121
  popup.container.remove();
125
122
  }
126
123
  }
127
- export function closePopupsById(map, id) {
128
- const removedPopups = remove(map.popups, (popup) => popup.id === id);
129
- for (const popup of removedPopups) {
124
+ export function closePopups(map) {
125
+ for (const popup of map.popups) {
130
126
  popup.popup.remove();
131
- if (isPlainObject(popup.options.content)) {
127
+ if (hasObjectContent(popup.options)) {
132
128
  render(null, popup.container);
133
129
  }
134
130
  popup.container.remove();
135
131
  }
132
+ map.popups = [];
136
133
  }
137
- export function removePopups(map) {
134
+ export function closePopupsByIds(map, ids) {
135
+ const toClose = [];
136
+ const toReRender = [];
138
137
  for (const popup of map.popups) {
138
+ const itemIndex = popup.ids.findIndex((id) => ids.includes(id));
139
+ if (itemIndex < 0)
140
+ continue;
141
+ if (popup.ids.length === 1) {
142
+ toClose.push(popup);
143
+ }
144
+ else if (popup.ids.length > 1) {
145
+ popup.ids.splice(itemIndex, 1);
146
+ popup.options.splice(itemIndex, 1);
147
+ toClose.push(popup);
148
+ toReRender.push(popup);
149
+ }
150
+ }
151
+ for (const popup of toClose) {
139
152
  popup.popup.remove();
140
- if (isPlainObject(popup.options.content)) {
153
+ if (hasObjectContent(popup.options)) {
141
154
  render(null, popup.container);
142
155
  }
143
156
  popup.container.remove();
157
+ map.popups = without(map.popups, popup);
158
+ }
159
+ for (const popup of toReRender) {
160
+ createNewPopup(map, popup.options);
144
161
  }
145
- map.popups = [];
146
162
  }
@@ -0,0 +1,14 @@
1
+ import type { MapkaMap, MapMapkaPopup } from "../map.js";
2
+ import type { MapkaPopupOptionsResolved } from "../types/popup.js";
3
+ export type ClosePopupAction = {
4
+ type: "close";
5
+ ids: string[];
6
+ };
7
+ export type CreatePopupAction = {
8
+ type: "create";
9
+ options: MapkaPopupOptionsResolved[];
10
+ };
11
+ export type PopupGroupAction = ClosePopupAction | CreatePopupAction;
12
+ export declare function actionsByChanges(popups: MapMapkaPopup[], newOptions: MapkaPopupOptionsResolved[]): PopupGroupAction[];
13
+ export declare function computePopupGroups(map: MapkaMap, newOptions: MapkaPopupOptionsResolved[]): PopupGroupAction[];
14
+ //# sourceMappingURL=popupGroups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popupGroups.d.ts","sourceRoot":"","sources":["../../src/modules/popupGroups.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAChE,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,yBAAyB,EAAE,CAAA;CAAE,CAAC;AAEzF,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAkJpE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EAAE,EACvB,UAAU,EAAE,yBAAyB,EAAE,GACtC,gBAAgB,EAAE,CAmBpB;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,UAAU,EAAE,yBAAyB,EAAE,GACtC,gBAAgB,EAAE,CASpB"}
@@ -0,0 +1,130 @@
1
+ import Supercluster from "supercluster";
2
+ const WORLD_BOUNDS = [-180, -90, 180, 90];
3
+ function clustersByLocation(previous, newOptions, zoom) {
4
+ const all = [...previous, ...newOptions];
5
+ const features = all.map(({ lngLat }, index) => ({
6
+ type: "Feature",
7
+ properties: { index },
8
+ geometry: {
9
+ type: "Point",
10
+ coordinates: lngLat,
11
+ },
12
+ }));
13
+ const sc = new Supercluster({
14
+ radius: 20,
15
+ extent: 512,
16
+ });
17
+ sc.load(features);
18
+ const results = sc.getClusters(WORLD_BOUNDS, zoom);
19
+ const clusters = [];
20
+ for (const { properties, geometry } of results) {
21
+ if ("cluster" in properties) {
22
+ const leaves = sc.getLeaves(properties.cluster_id, Infinity);
23
+ clusters.push({
24
+ options: leaves.map((point) => all[point.properties.index]),
25
+ lngLat: geometry.coordinates,
26
+ });
27
+ }
28
+ else {
29
+ const opt = all[properties.index];
30
+ clusters.push({
31
+ options: [opt],
32
+ lngLat: opt.lngLat,
33
+ });
34
+ }
35
+ }
36
+ return clusters;
37
+ }
38
+ function clustersFromPopups(notUpdated, popups) {
39
+ const clusters = [];
40
+ for (const popup of popups) {
41
+ clusters.push({
42
+ options: popup.options.filter((opt) => notUpdated.includes(opt)),
43
+ lngLat: popup.popup.getLngLat().toArray(),
44
+ });
45
+ }
46
+ return clusters.filter((cluster) => cluster.options.length > 0);
47
+ }
48
+ function clusterKey(cluster) {
49
+ return cluster.options
50
+ .map((o) => o.id)
51
+ .sort()
52
+ .join("-");
53
+ }
54
+ function actionsByClustersChanges(prev, next) {
55
+ const prevByKey = new Map(prev.map((c) => [clusterKey(c), c]));
56
+ const nextByKey = new Map(next.map((c) => [clusterKey(c), c]));
57
+ const close = [];
58
+ const create = [];
59
+ for (const [key, cluster] of prevByKey) {
60
+ if (!nextByKey.has(key)) {
61
+ close.push({
62
+ type: "close",
63
+ ids: cluster.options.map((o) => o.id),
64
+ });
65
+ }
66
+ }
67
+ for (const [key, cluster] of nextByKey) {
68
+ if (!prevByKey.has(key)) {
69
+ create.push({
70
+ type: "create",
71
+ options: cluster.options,
72
+ });
73
+ }
74
+ }
75
+ return { close, create };
76
+ }
77
+ function actionsByProximity(popups, newOptions, zoom) {
78
+ const updated = [];
79
+ const nonUpdated = [];
80
+ for (const popup of popups) {
81
+ for (const opt of popup.options) {
82
+ if (newOptions.find((o) => o.id === opt.id)) {
83
+ updated.push(opt);
84
+ }
85
+ else {
86
+ nonUpdated.push(opt);
87
+ }
88
+ }
89
+ }
90
+ const prevClusters = clustersFromPopups(nonUpdated, popups);
91
+ const nextClusters = clustersByLocation(nonUpdated, newOptions, zoom);
92
+ const { close, create } = actionsByClustersChanges(prevClusters, nextClusters);
93
+ const updatedIds = updated.map((opt) => opt.id);
94
+ const closeIds = close.flatMap((opt) => opt.ids);
95
+ return [
96
+ {
97
+ type: "close",
98
+ ids: updatedIds.concat(closeIds),
99
+ },
100
+ ...create,
101
+ ];
102
+ }
103
+ export function actionsByChanges(popups, newOptions) {
104
+ const updated = [];
105
+ for (const popup of popups) {
106
+ if (newOptions.find((o) => popup.ids.includes(o.id))) {
107
+ updated.push(popup);
108
+ }
109
+ }
110
+ return [
111
+ ...updated.map((popup) => ({
112
+ type: "close",
113
+ ids: popup.ids,
114
+ })),
115
+ {
116
+ type: "create",
117
+ options: newOptions,
118
+ },
119
+ ];
120
+ }
121
+ export function computePopupGroups(map, newOptions) {
122
+ const popups = map.getPopups();
123
+ const zoom = map.getZoom();
124
+ if (map.scrollPopups) {
125
+ return actionsByProximity(popups, newOptions, zoom);
126
+ }
127
+ else {
128
+ return actionsByChanges(popups, newOptions);
129
+ }
130
+ }