@mapka/maplibre-gl-sdk 0.16.3 → 0.17.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 (43) hide show
  1. package/README.md +29 -2
  2. package/lib/.buildInfo.json +1 -1
  3. package/lib/components/PopupList.d.ts.map +1 -1
  4. package/lib/components/PopupList.js +26 -2
  5. package/lib/components/icons/ChevronDownIcon.d.ts +2 -0
  6. package/lib/components/icons/ChevronDownIcon.d.ts.map +1 -0
  7. package/lib/components/icons/ChevronDownIcon.js +4 -0
  8. package/lib/components/icons/ChevronUpIcon.d.ts +2 -0
  9. package/lib/components/icons/ChevronUpIcon.d.ts.map +1 -0
  10. package/lib/components/icons/ChevronUpIcon.js +4 -0
  11. package/lib/index.d.ts +1 -0
  12. package/lib/index.d.ts.map +1 -1
  13. package/lib/index.js +1 -0
  14. package/lib/map.d.ts.map +1 -1
  15. package/lib/modules/icons.d.ts +8 -4
  16. package/lib/modules/icons.d.ts.map +1 -1
  17. package/lib/modules/icons.js +69 -31
  18. package/lib/modules/markers.d.ts.map +1 -1
  19. package/lib/modules/markers.js +55 -2
  20. package/lib/modules/popupGroups.d.ts.map +1 -1
  21. package/lib/styles.css +1 -1
  22. package/lib/types/marker.d.ts +4 -2
  23. package/lib/types/marker.d.ts.map +1 -1
  24. package/package.json +3 -2
  25. package/src/components/PopupContent.css +7 -5
  26. package/src/components/PopupList.css +59 -12
  27. package/src/components/PopupList.tsx +83 -8
  28. package/src/components/icons/ChevronDownIcon.tsx +20 -0
  29. package/src/components/icons/ChevronUpIcon.tsx +20 -0
  30. package/src/controls/MapkaDrawControl.css +1 -3
  31. package/src/index.ts +2 -0
  32. package/src/map.ts +7 -1
  33. package/src/modules/icons.ts +74 -38
  34. package/src/modules/markers.css +18 -0
  35. package/src/modules/markers.ts +67 -3
  36. package/src/modules/popupGroups.ts +0 -1
  37. package/src/styles.css +1 -1
  38. package/src/types/marker.ts +4 -2
  39. package/lib/components/PopupListItem.d.ts +0 -13
  40. package/lib/components/PopupListItem.d.ts.map +0 -1
  41. package/lib/components/PopupListItem.js +0 -11
  42. package/src/components/PopupListItem.css +0 -61
  43. package/src/components/PopupListItem.tsx +0 -40
@@ -1 +1 @@
1
- {"version":3,"file":"PopupList.d.ts","sourceRoot":"","sources":["../../src/components/PopupList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAGnE,UAAU,oBAAoB;IAC5B,KAAK,EAAE,yBAAyB,EAAE,CAAC;CACpC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,eAEnE;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,oBAAoB,gCAexD"}
1
+ {"version":3,"file":"PopupList.d.ts","sourceRoot":"","sources":["../../src/components/PopupList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAMnE,UAAU,oBAAoB;IAC5B,KAAK,EAAE,yBAAyB,EAAE,CAAC;CACpC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,eAEnE;AA0CD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,oBAAoB,gCA8CxD"}
@@ -1,8 +1,32 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { PopupListItem } from "./PopupListItem.js";
2
+ import { useState } from "preact/hooks";
3
+ import { PopupContent } from "./PopupContent.js";
4
+ import { ChevronUpIcon } from "./icons/ChevronUpIcon.js";
5
+ import { ChevronDownIcon } from "./icons/ChevronDownIcon.js";
6
+ import { noop } from "es-toolkit";
3
7
  export function PopupCustomElement({ popup }) {
4
8
  return popup;
5
9
  }
10
+ function PopupListNav({ index, total, onPrev, onNext }) {
11
+ const isFirst = index === 0;
12
+ const isLast = index === total - 1;
13
+ return (_jsxs("div", { class: "mapka-popup-list-nav", children: [_jsx("button", { type: "button", class: "mapka-popup-list-nav-btn", onClick: onPrev, disabled: isFirst, "aria-label": "Previous popup", children: _jsx(ChevronUpIcon, {}) }), _jsxs("div", { class: "mapka-popup-list-nav-counter", children: [_jsx("span", { children: index + 1 }), _jsx("span", { class: "mapka-popup-list-nav-counter-divider" }), _jsx("span", { children: total })] }), _jsx("button", { type: "button", class: "mapka-popup-list-nav-btn", onClick: onNext, disabled: isLast, "aria-label": "Next popup", children: _jsx(ChevronDownIcon, {}) })] }));
14
+ }
6
15
  export function PopupList({ items }) {
7
- return (_jsxs("div", { class: "mapka-popup-list-wrapper", children: [_jsx("div", { class: "mapka-popup-list", children: items.map(({ content, id }) => content instanceof HTMLElement ? (_jsx(PopupCustomElement, { popup: content }, id)) : (_jsx(PopupListItem, { popup: content }, id))) }), _jsx("div", { class: "mapka-popup-list-gradient" })] }));
16
+ const [index, setIndex] = useState(0);
17
+ const safeIndex = Math.min(index, items.length - 1);
18
+ const handlePrev = (e) => {
19
+ e.stopPropagation();
20
+ if (safeIndex > 0) {
21
+ setIndex(safeIndex - 1);
22
+ }
23
+ };
24
+ const handleNext = (e) => {
25
+ e.stopPropagation();
26
+ if (safeIndex < items.length - 1) {
27
+ setIndex(safeIndex + 1);
28
+ }
29
+ };
30
+ const { id, content } = items[safeIndex];
31
+ return (_jsxs("div", { class: "mapka-popup-list-wrapper", children: [_jsx("div", { class: "mapka-popup-list", children: content instanceof HTMLElement ? (_jsx(PopupCustomElement, { popup: content }, id)) : (_jsx(PopupContent, { title: content.title, description: content.description, rows: content.rows, imageUrls: content.imageUrls, primaryAction: content.primaryAction, closeButton: false, onClose: noop }, id)) }), items.length > 1 && (_jsx(PopupListNav, { index: safeIndex, total: items.length, onPrev: handlePrev, onNext: handleNext }))] }));
8
32
  }
@@ -0,0 +1,2 @@
1
+ export declare function ChevronDownIcon(): import("preact").JSX.Element;
2
+ //# sourceMappingURL=ChevronDownIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChevronDownIcon.d.ts","sourceRoot":"","sources":["../../../src/components/icons/ChevronDownIcon.tsx"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,iCAmB9B"}
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ export function ChevronDownIcon() {
3
+ return (_jsx("svg", { viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", class: "mapka-popup-icon mapka-popup-icon-sm", children: _jsx("path", { d: "M3 6 7.3 10.3a1 1 0 0 0 1.4 0L13 6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }));
4
+ }
@@ -0,0 +1,2 @@
1
+ export declare function ChevronUpIcon(): import("preact").JSX.Element;
2
+ //# sourceMappingURL=ChevronUpIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChevronUpIcon.d.ts","sourceRoot":"","sources":["../../../src/components/icons/ChevronUpIcon.tsx"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,iCAmB5B"}
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ export function ChevronUpIcon() {
3
+ return (_jsx("svg", { viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", focusable: "false", class: "mapka-popup-icon mapka-popup-icon-sm", children: _jsx("path", { d: "M3 10 7.3 5.7a1 1 0 0 1 1.4 0L13 10", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }));
4
+ }
package/lib/index.d.ts CHANGED
@@ -9,4 +9,5 @@ export { MapkaMap as Map } from "./map.js";
9
9
  export { MapkaMapOptions as MapOptions } from "./map.js";
10
10
  export { MapkaExportControl, MapkaExportControlOptions, } from "./controls/MapkaExportControl.js";
11
11
  export { MapkaDrawControl, MapkaDrawControlOptions, DrawMode, } from "./controls/MapkaDrawControl.js";
12
+ export { loadMarkerIcon } from "./modules/icons.js";
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,QAAQ,IAAI,GAAG,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,IAAI,UAAU,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EACL,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,QAAQ,GACT,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,QAAQ,IAAI,GAAG,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,IAAI,UAAU,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EACL,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,QAAQ,GACT,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
package/lib/index.js CHANGED
@@ -8,3 +8,4 @@ export * from "./constants/styles.js";
8
8
  export { MapkaMap as Map } from "./map.js";
9
9
  export { MapkaExportControl, } from "./controls/MapkaExportControl.js";
10
10
  export { MapkaDrawControl, } from "./controls/MapkaDrawControl.js";
11
+ export { loadMarkerIcon } from "./modules/icons.js";
package/lib/map.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAW1C,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EAGL,UAAU,EAEV,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAChC;AA0BD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACrC,SAAS,EAAE,WAAW,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,UAAU,MAAM;IACd,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC;AAED,qBAAa,QAAS,SAAQ,UAAU,CAAC,GAAG;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,CAAU;IAErB,MAAM,EAAE,MAAM,CAAW;IACzB,OAAO,EAAE,cAAc,EAAE,CAAM;IAE/B,SAAS,EAAE,MAAM,CAAK;IACtB,YAAY,EAAE,OAAO,CAAQ;IAC7B,MAAM,EAAE,aAAa,EAAE,CAAM;gBAEjB,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAa,EAAE,YAAmB,EAAE,GAAG,OAAO,EAAE,EAAE,eAAe;IA0BzG,QAAQ,CACb,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAClC,OAAO,GAAE,gBAAgB,GAAG,YAAiB;IAexC,UAAU,CAAC,OAAO,EAAE,kBAAkB,EAAE;IAIxC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE;IAI3C,aAAa;IAIb,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,EAAE;IAIxD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE;IAIhC,WAAW,CAAC,KAAK,EAAE,iBAAiB;IAIpC,SAAS;IAIT,YAAY;IAIN,MAAM,CAAC,OAAO,CAAC,EAAE,kBAAkB;IAIzC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAIrE"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAW1C,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EAGL,UAAU,EAEV,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAChC;AA0BD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACrC,SAAS,EAAE,WAAW,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,UAAU,MAAM;IACd,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC;AAED,qBAAa,QAAS,SAAQ,UAAU,CAAC,GAAG;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,CAAU;IAErB,MAAM,EAAE,MAAM,CAAW;IACzB,OAAO,EAAE,cAAc,EAAE,CAAM;IAE/B,SAAS,EAAE,MAAM,CAAK;IACtB,YAAY,EAAE,OAAO,CAAQ;IAC7B,MAAM,EAAE,aAAa,EAAE,CAAM;gBAEjB,EACjB,gBAAgB,EAChB,MAAM,EACN,SAAa,EACb,YAAmB,EACnB,GAAG,OAAO,EACX,EAAE,eAAe;IA0BX,QAAQ,CACb,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAClC,OAAO,GAAE,gBAAgB,GAAG,YAAiB;IAexC,UAAU,CAAC,OAAO,EAAE,kBAAkB,EAAE;IAIxC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE;IAI3C,aAAa;IAIb,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,EAAE;IAIxD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE;IAIhC,WAAW,CAAC,KAAK,EAAE,iBAAiB;IAIpC,SAAS;IAIT,YAAY;IAIN,MAAM,CAAC,OAAO,CAAC,EAAE,kBAAkB;IAIzC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAIrE"}
@@ -1,10 +1,14 @@
1
1
  import type { MapkaMap } from "../map.js";
2
2
  import type { MapStyleImageMissingEvent } from "maplibre-gl";
3
3
  /**
4
- * Load any icons that are missing from the map
5
- * Only supports icons from Mapka API maki, temaki and tabler
6
- * @param map
7
- * @param event
4
+ * Fetch an icon's SVG source from the Mapka API.
5
+ * Batched and cached across callers; concurrent requests for the same
6
+ * id share one fetch.
7
+ */
8
+ export declare function loadMarkerIcon(id: string): Promise<string>;
9
+ /**
10
+ * Load any icons that are missing from the map from the Mapka API.
11
+ * @see https://github.com/mapbox/mapbox-gl-js/issues/5529
8
12
  */
9
13
  export declare function loadLayersIcons(map: MapkaMap, event: MapStyleImageMissingEvent): void;
10
14
  //# sourceMappingURL=icons.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/modules/icons.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAsC7D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,QAS9E"}
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/modules/icons.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AA8C7D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY1D;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,QAiB9E"}
@@ -1,43 +1,81 @@
1
+ import ky from "ky";
1
2
  import { debounce, uniq } from "es-toolkit";
2
3
  import { getMapkaUrl } from "../utils/url.js";
3
- let missingIcons = [];
4
- let loadedIcons = [];
4
+ const svgCache = new Map();
5
+ const pendingResolvers = new Map();
6
+ let pendingIds = [];
5
7
  /**
6
- * Load missing icons from Mapka API
7
- * Inspired by https://github.com/mapbox/mapbox-gl-js/issues/5529
8
+ * Debounced batch fetch: pulls unique pending ids, hits the icons API
9
+ * in one request, populates svgCache, and resolves waiting callers.
8
10
  */
9
- const loadIcons = debounce((map) => {
10
- if (missingIcons.length === 0) {
11
+ const flushIcons = debounce(() => {
12
+ const idsToFetch = uniq(pendingIds).filter((id) => !svgCache.has(id));
13
+ if (idsToFetch.length === 0)
11
14
  return;
12
- }
13
- const iconsToLoad = uniq(missingIcons).filter((id) => !loadedIcons.includes(id));
14
- loadedIcons = loadedIcons.concat(iconsToLoad);
15
- missingIcons = [];
16
- if (iconsToLoad.length === 0) {
17
- return;
18
- }
19
- const search = new URLSearchParams(iconsToLoad.map((id) => ["ids", id]));
20
- fetch(`${getMapkaUrl()}/v1/icons?${search}`)
21
- .then((response) => response.json())
15
+ pendingIds = [];
16
+ ky.get(`${getMapkaUrl()}/v1/icons`, {
17
+ searchParams: idsToFetch.map((id) => ["ids", id]),
18
+ })
19
+ .json()
22
20
  .then((data) => {
23
- data.forEach(({ svg, id }) => {
24
- const img = new Image(15, 15);
25
- img.onload = () => map.addImage(id, img);
26
- img.src = `data:image/svg+xml;charset=utf-8;base64,${btoa(svg)}`;
27
- });
21
+ for (const { id, svg } of data) {
22
+ svgCache.set(id, svg);
23
+ const resolver = pendingResolvers.get(id);
24
+ if (resolver) {
25
+ resolver.resolve(svg);
26
+ pendingResolvers.delete(id);
27
+ }
28
+ }
29
+ })
30
+ .catch((err) => {
31
+ for (const id of idsToFetch) {
32
+ const resolver = pendingResolvers.get(id);
33
+ if (resolver) {
34
+ resolver.reject(err);
35
+ pendingResolvers.delete(id);
36
+ }
37
+ }
28
38
  });
29
39
  }, 50);
30
40
  /**
31
- * Load any icons that are missing from the map
32
- * Only supports icons from Mapka API maki, temaki and tabler
33
- * @param map
34
- * @param event
41
+ * Fetch an icon's SVG source from the Mapka API.
42
+ * Batched and cached across callers; concurrent requests for the same
43
+ * id share one fetch.
44
+ */
45
+ export function loadMarkerIcon(id) {
46
+ const cached = svgCache.get(id);
47
+ if (cached)
48
+ return Promise.resolve(cached);
49
+ const existing = pendingResolvers.get(id);
50
+ if (existing)
51
+ return existing.promise;
52
+ const resolver = Promise.withResolvers();
53
+ pendingResolvers.set(id, resolver);
54
+ pendingIds.push(id);
55
+ flushIcons();
56
+ return resolver.promise;
57
+ }
58
+ const isStyleImage = (id) => id.includes(":");
59
+ /**
60
+ * Load any icons that are missing from the map from the Mapka API.
61
+ * @see https://github.com/mapbox/mapbox-gl-js/issues/5529
35
62
  */
36
63
  export function loadLayersIcons(map, event) {
37
- if (event.id.startsWith("maki:") ||
38
- event.id.startsWith("temaki:") ||
39
- event.id.startsWith("tabler:")) {
40
- missingIcons.push(event.id);
41
- }
42
- loadIcons(map);
64
+ if (map.hasImage(event.id) || !isStyleImage(event.id))
65
+ return;
66
+ loadMarkerIcon(event.id)
67
+ .then((svg) => {
68
+ if (map.hasImage(event.id))
69
+ return;
70
+ const img = new Image(15, 15);
71
+ img.onload = () => {
72
+ if (!map.hasImage(event.id)) {
73
+ map.addImage(event.id, img);
74
+ }
75
+ };
76
+ img.src = `data:image/svg+xml;charset=utf-8;base64,${btoa(svg)}`;
77
+ })
78
+ .catch((err) => {
79
+ map.logger.warn(`[mapka] Failed to load layer icon "${event.id}":`, err);
80
+ });
43
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/modules/markers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAU,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAkB7D,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAElD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAE/E;AAmFD,wBAAgB,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAcpF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAK9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAKhF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,QAK1C;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,QAK5C;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,QAG1E"}
1
+ {"version":3,"file":"markers.d.ts","sourceRoot":"","sources":["../../src/modules/markers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAyB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAwB7D,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAElD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAE/E;AAqID,wBAAgB,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAqBpF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,QAK9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,QAKhF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,QAK1C;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,QAK5C;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,kBAAkB,QAG1E"}
@@ -1,6 +1,12 @@
1
1
  import { Marker } from "maplibre-gl";
2
2
  import { get } from "es-toolkit/compat";
3
3
  import { remove } from "es-toolkit";
4
+ import { loadMarkerIcon } from "./icons.js";
5
+ /**
6
+ * Offset for the default pin so the tip sits on the LngLat anchor point.
7
+ * Value taken from maplibre-gl-js: (shadow translate-y + ellipse cy) - (height/2) ≈ 14.
8
+ */
9
+ const DEFAULT_PIN_OFFSET = [0, -14];
4
10
  /**
5
11
  * Default marker offset
6
12
  * @see https://github.com/maplibre/maplibre-gl-js/blob/master/src/ui/marker.ts#L457
@@ -90,10 +96,57 @@ function setupMarkerPopupListeners(map, marker, popup, options) {
90
96
  });
91
97
  }
92
98
  }
99
+ /**
100
+ * Apply `color` (as SVG `fill`) to the fetched icon SVG inside `element`.
101
+ *
102
+ * Override every descendant whose `fill` attribute is set and not `"none"`,
103
+ * then set `fill` on the root `<svg>` itself so paths that omit the
104
+ * attribute (common in maki — they inherit the browser default) pick up the
105
+ * user color via SVG cascading. `fill="none"` is preserved so outline-only
106
+ * shapes keep their transparency.
107
+ *
108
+ * Only invoked for icon markers; the default maplibre pin handles its own
109
+ * color via `MarkerOptions.color`.
110
+ */
111
+ function applyMarkerColors(element, color) {
112
+ if (!color) {
113
+ return;
114
+ }
115
+ for (const el of element.querySelectorAll('[fill]:not([fill="none"])')) {
116
+ el.setAttribute("fill", color);
117
+ }
118
+ const svg = element.querySelector("svg");
119
+ if (svg && !svg.hasAttribute("fill")) {
120
+ svg.setAttribute("fill", color);
121
+ }
122
+ }
123
+ function createMarkerElement(currentMap, options) {
124
+ const { color, icon } = options;
125
+ if (!icon) {
126
+ return;
127
+ }
128
+ const element = document.createElement("div");
129
+ element.className = "mapka-marker-icon";
130
+ element.dataset.iconId = icon;
131
+ loadMarkerIcon(icon)
132
+ .then((svg) => {
133
+ element.innerHTML = svg;
134
+ applyMarkerColors(element, color);
135
+ })
136
+ .catch((err) => {
137
+ currentMap.logger.warn(`[mapka] Failed to load marker icon "${icon}":`, err);
138
+ });
139
+ return element;
140
+ }
93
141
  export function addMarkers(currentMap, markersOptions) {
94
142
  for (const markerOptions of markersOptions) {
95
- const { lngLat, popup, ...options } = markerOptions;
96
- const newMarker = new Marker(options).setLngLat(lngLat).addTo(currentMap);
143
+ const { lngLat, popup, icon, offset = DEFAULT_PIN_OFFSET, ...rest } = markerOptions;
144
+ const markerOpts = {
145
+ ...rest,
146
+ element: createMarkerElement(currentMap, markerOptions),
147
+ offset,
148
+ };
149
+ const newMarker = new Marker(markerOpts).setLngLat(lngLat).addTo(currentMap);
97
150
  currentMap.markers.push({
98
151
  id: getMarkerId(markerOptions),
99
152
  options: markerOptions,
@@ -1 +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"}
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;AAiJpE,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"}