@mapka/maplibre-gl-sdk 0.11.0 → 0.13.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 (41) hide show
  1. package/README.md +325 -3
  2. package/lib/.buildInfo.json +1 -1
  3. package/lib/components/DownloadIcon.d.ts +4 -0
  4. package/lib/components/DownloadIcon.d.ts.map +1 -0
  5. package/lib/components/DownloadIcon.js +10 -0
  6. package/lib/components/PopupContent.d.ts.map +1 -1
  7. package/lib/components/ProgressDownIcon.d.ts +4 -0
  8. package/lib/components/ProgressDownIcon.d.ts.map +1 -0
  9. package/lib/components/ProgressDownIcon.js +14 -0
  10. package/lib/controls/MapkaExportControl.d.ts +21 -0
  11. package/lib/controls/MapkaExportControl.d.ts.map +1 -0
  12. package/lib/controls/MapkaExportControl.js +76 -0
  13. package/lib/index.d.ts +2 -0
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.js +2 -0
  16. package/lib/map.d.ts +13 -3
  17. package/lib/map.d.ts.map +1 -1
  18. package/lib/map.js +16 -8
  19. package/lib/modules/export.d.ts +4 -0
  20. package/lib/modules/export.d.ts.map +1 -0
  21. package/lib/modules/export.js +40 -0
  22. package/lib/modules/markers.d.ts +1 -1
  23. package/lib/modules/markers.d.ts.map +1 -1
  24. package/lib/modules/markers.js +1 -1
  25. package/lib/modules/popup.d.ts +3 -2
  26. package/lib/modules/popup.d.ts.map +1 -1
  27. package/lib/modules/popup.js +13 -5
  28. package/lib/types/export.d.ts +7 -0
  29. package/lib/types/export.d.ts.map +1 -0
  30. package/lib/types/export.js +1 -0
  31. package/package.json +3 -2
  32. package/src/components/DownloadIcon.tsx +25 -0
  33. package/src/components/PopupContent.tsx +1 -0
  34. package/src/components/ProgressDownIcon.tsx +29 -0
  35. package/src/controls/MapkaExportControl.tsx +111 -0
  36. package/src/index.ts +5 -0
  37. package/src/map.ts +25 -7
  38. package/src/modules/export.ts +55 -0
  39. package/src/modules/markers.ts +1 -1
  40. package/src/modules/popup.tsx +21 -23
  41. package/src/types/export.ts +6 -0
@@ -0,0 +1,4 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+ export declare const DownloadIcon: () => h.JSX.Element;
4
+ //# sourceMappingURL=DownloadIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DownloadIcon.d.ts","sourceRoot":"","sources":["../../src/components/DownloadIcon.tsx"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,eAAO,MAAM,YAAY,qBAqBxB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+ export const DownloadIcon = () => {
4
+ return (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-label": "Download" },
5
+ h("title", null, "Download"),
6
+ h("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
7
+ h("path", { d: "M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2" }),
8
+ h("path", { d: "M7 11l5 5l5 -5" }),
9
+ h("path", { d: "M12 4l0 12" })));
10
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"PopupContent.d.ts","sourceRoot":"","sources":["../../src/components/PopupContent.tsx"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,CAAC,EAAY,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,UAAW,SAAQ,iBAAiB;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8LD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,OAAO,GACR,EAAE,UAAU,iBAqBZ"}
1
+ {"version":3,"file":"PopupContent.d.ts","sourceRoot":"","sources":["../../src/components/PopupContent.tsx"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,CAAC,EAAY,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,UAAW,SAAQ,iBAAiB;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8LD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,OAAO,GACR,EAAE,UAAU,iBAqBZ"}
@@ -0,0 +1,4 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+ export declare const ProgressDownIcon: () => h.JSX.Element;
4
+ //# sourceMappingURL=ProgressDownIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressDownIcon.d.ts","sourceRoot":"","sources":["../../src/components/ProgressDownIcon.tsx"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,eAAO,MAAM,gBAAgB,qBAyB5B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+ export const ProgressDownIcon = () => {
4
+ return (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "icon icon-tabler icons-tabler-outline icon-tabler-progress-down" },
5
+ h("title", null, "Progress Download"),
6
+ h("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
7
+ h("path", { d: "M10 20.777a8.942 8.942 0 0 1 -2.48 -.969" }),
8
+ h("path", { d: "M14 3.223a9.003 9.003 0 0 1 0 17.554" }),
9
+ h("path", { d: "M4.579 17.093a8.961 8.961 0 0 1 -1.227 -2.592" }),
10
+ h("path", { d: "M3.124 10.5c.16 -.95 .468 -1.85 .9 -2.675l.169 -.305" }),
11
+ h("path", { d: "M6.907 4.579a8.954 8.954 0 0 1 3.093 -1.356" }),
12
+ h("path", { d: "M12 9v6" }),
13
+ h("path", { d: "M15 12l-3 3l-3 -3" })));
14
+ };
@@ -0,0 +1,21 @@
1
+ import type { IControl } from "maplibre-gl";
2
+ import type { MapkaMap } from "../map.js";
3
+ import type { MapkaExportOptions } from "../types/export.js";
4
+ export interface MapkaExportControlOptions extends MapkaExportOptions {
5
+ filename?: string;
6
+ }
7
+ export declare class MapkaExportControl implements IControl {
8
+ private map;
9
+ private container;
10
+ private options;
11
+ private isExporting;
12
+ constructor(options?: MapkaExportControlOptions);
13
+ private downloadImage;
14
+ private handleError;
15
+ private onClick;
16
+ private render;
17
+ private unmount;
18
+ onAdd(map: MapkaMap): HTMLElement;
19
+ onRemove(): void;
20
+ }
21
+ //# sourceMappingURL=MapkaExportControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapkaExportControl.d.ts","sourceRoot":"","sources":["../../src/controls/MapkaExportControl.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,GAAE,yBAA8B;IAOnD,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,OAAO,CAmBb;IAEF,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,OAAO;IAQR,KAAK,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW;IAWjC,QAAQ,IAAI,IAAI;CAQxB"}
@@ -0,0 +1,76 @@
1
+ // biome-ignore lint/correctness/noUnusedImports: preact jsx
2
+ import { h } from "preact";
3
+ import { render } from "preact";
4
+ import { DownloadIcon } from "../components/DownloadIcon.js";
5
+ import { ProgressDownIcon } from "../components/ProgressDownIcon.js";
6
+ const Button = ({ isExporting, onClick }) => {
7
+ return (h("button", { type: "button", className: "maplibregl-ctrl maplibregl-ctrl-group", title: "Export map as PNG", "aria-label": "Export map as PNG", disabled: isExporting, onClick: onClick }, isExporting ? h(ProgressDownIcon, null) : h(DownloadIcon, null)));
8
+ };
9
+ export class MapkaExportControl {
10
+ map;
11
+ container;
12
+ options;
13
+ isExporting = false;
14
+ constructor(options = {}) {
15
+ this.options = {
16
+ filename: "map-export",
17
+ ...options,
18
+ };
19
+ }
20
+ downloadImage(img) {
21
+ const link = document.createElement("a");
22
+ link.download = `${this.options.filename}.png`;
23
+ link.href = img.src;
24
+ link.click();
25
+ link.remove();
26
+ }
27
+ handleError(error) {
28
+ this.map?.logger.error(error, "Failed to export map image");
29
+ }
30
+ onClick = async () => {
31
+ if (!this.map || this.isExporting)
32
+ return;
33
+ this.isExporting = true;
34
+ this.render();
35
+ this.map
36
+ .export({
37
+ hideControls: true,
38
+ hideMarkers: false,
39
+ hidePopups: false,
40
+ ...this.options,
41
+ })
42
+ .then((img) => this.downloadImage(img))
43
+ .catch((error) => this.handleError(error))
44
+ .finally(() => {
45
+ this.isExporting = false;
46
+ this.render();
47
+ });
48
+ };
49
+ render() {
50
+ if (!this.container) {
51
+ this.map?.logger.error("Export control container not found for rendering");
52
+ return;
53
+ }
54
+ render(h(Button, { isExporting: this.isExporting, onClick: this.onClick }), this.container);
55
+ }
56
+ unmount() {
57
+ if (!this.container) {
58
+ this.map?.logger.error("Export control container not found during unmount");
59
+ return;
60
+ }
61
+ render(null, this.container);
62
+ }
63
+ onAdd(map) {
64
+ this.map = map;
65
+ this.container = document.createElement("div");
66
+ this.container.className = "mapka-export-control";
67
+ this.render();
68
+ return this.container;
69
+ }
70
+ onRemove() {
71
+ this.unmount();
72
+ this.container?.remove?.();
73
+ this.container = undefined;
74
+ this.map = undefined;
75
+ }
76
+ }
package/lib/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export * from "maplibre-gl";
2
2
  export * from "./types/layer.js";
3
3
  export * from "./types/marker.js";
4
4
  export * from "./types/style.js";
5
+ export * from "./types/export.js";
5
6
  export { MapkaMap as Map } from "./map.js";
6
7
  export { MapkaMapOptions as MapOptions } from "./map.js";
8
+ export { MapkaExportControl, MapkaExportControlOptions, } from "./controls/MapkaExportControl.js";
7
9
  //# 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;AAEjC,OAAO,EAAE,QAAQ,IAAI,GAAG,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,eAAe,IAAI,UAAU,EAAE,MAAM,UAAU,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,mBAAmB,CAAC;AAElC,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"}
package/lib/index.js CHANGED
@@ -2,4 +2,6 @@ export * from "maplibre-gl";
2
2
  export * from "./types/layer.js";
3
3
  export * from "./types/marker.js";
4
4
  export * from "./types/style.js";
5
+ export * from "./types/export.js";
5
6
  export { MapkaMap as Map } from "./map.js";
7
+ export { MapkaExportControl, } from "./controls/MapkaExportControl.js";
package/lib/map.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as maplibregl from "maplibre-gl";
2
2
  import type { Marker, Popup, MapOptions, StyleSwapOptions, StyleOptions, StyleSpecification } from "maplibre-gl";
3
3
  import type { MapkaMarkerOptions, MapkaPopupOptions } from "./types/marker.js";
4
+ import type { MapkaExportOptions } from "./types/export.js";
4
5
  export interface MapkaMapOptions extends MapOptions {
5
6
  maxPopups?: number;
6
7
  apiKey: string;
@@ -17,8 +18,14 @@ export type MapMapkaMarker = {
17
18
  options: MapkaMarkerOptions;
18
19
  marker: Marker;
19
20
  };
21
+ interface Logger {
22
+ log: (...args: unknown[]) => void;
23
+ warn: (...args: unknown[]) => void;
24
+ error: (...args: unknown[]) => void;
25
+ }
20
26
  export declare class MapkaMap extends maplibregl.Map {
21
27
  static env: string;
28
+ logger: Logger;
22
29
  markers: MapMapkaMarker[];
23
30
  maxPopups: number;
24
31
  popups: MapMapkaPopup[];
@@ -26,9 +33,12 @@ export declare class MapkaMap extends maplibregl.Map {
26
33
  setStyle(style: string | StyleSpecification, options?: StyleSwapOptions & StyleOptions): this;
27
34
  addMarkers(markers: MapkaMarkerOptions[]): void;
28
35
  updateMarkers(markers: MapkaMarkerOptions[]): void;
29
- clearMarkers(): void;
30
- openPopup(popup: MapkaPopupOptions, id?: string): string;
36
+ removeMarkers(): void;
37
+ openPopup(popup: MapkaPopupOptions): string;
31
38
  closePopup(id: string): void;
32
- updatePopup(popup: MapkaPopupOptions, id?: string): void;
39
+ updatePopup(popup: MapkaPopupOptions): void;
40
+ removePopups(): void;
41
+ export(options?: MapkaExportOptions): Promise<HTMLImageElement>;
33
42
  }
43
+ export {};
34
44
  //# sourceMappingURL=map.d.ts.map
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;AAgB1C,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EAGL,UAAU,EAEV,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE/E,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAChC;AA0BD,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,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,qBAAa,QAAS,SAAQ,UAAU,CAAC,GAAG;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,CAAU;IAErB,OAAO,EAAE,cAAc,EAAE,CAAM;IAE/B,SAAS,EAAE,MAAM,CAAK;IACtB,MAAM,EAAE,aAAa,EAAE,CAAM;gBAEjB,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAa,EAAE,GAAG,OAAO,EAAE,EAAE,eAAe;IAoBpF,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,YAAY;IAIZ,SAAS,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAAE,GAAE,MAA0B;IAIlE,UAAU,CAAC,EAAE,EAAE,MAAM;IAIrB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAAE,GAAE,MAA0B;CAG5E"}
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAiB1C,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EAGL,UAAU,EAEV,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAChC;AA0BD,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,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,MAAM,EAAE,aAAa,EAAE,CAAM;gBAEjB,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAa,EAAE,GAAG,OAAO,EAAE,EAAE,eAAe;IAoBpF,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;IAIlC,UAAU,CAAC,EAAE,EAAE,MAAM;IAIrB,WAAW,CAAC,KAAK,EAAE,iBAAiB;IAIpC,YAAY;IAIN,MAAM,CAAC,OAAO,CAAC,EAAE,kBAAkB;CAGjD"}
package/lib/map.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as maplibregl from "maplibre-gl";
2
2
  import { loadLayersIcons } from "./modules/icons.js";
3
- import { closeOnMapClickPopups, closePopupsById, getPopupId, openPopup, updatePopup, } from "./modules/popup.js";
4
- import { addMarkers, addStyleDiffMarkers, addStyleMarkers, clearMarkers, updateMarkers, } from "./modules/markers.js";
3
+ import { closeOnMapClickPopups, closePopupsById, openPopup, removePopups, updatePopup, } from "./modules/popup.js";
4
+ import { addMarkers, addStyleDiffMarkers, addStyleMarkers, removeMarkers, updateMarkers, } from "./modules/markers.js";
5
+ import { exportMap } from "./modules/export.js";
5
6
  const noopTransformRequest = (url) => {
6
7
  return {
7
8
  url,
@@ -23,6 +24,7 @@ const noopTransformStyle = (_, next) => {
23
24
  };
24
25
  export class MapkaMap extends maplibregl.Map {
25
26
  static env = "prod";
27
+ logger = console;
26
28
  markers = [];
27
29
  maxPopups = 1;
28
30
  popups = [];
@@ -59,16 +61,22 @@ export class MapkaMap extends maplibregl.Map {
59
61
  updateMarkers(markers) {
60
62
  updateMarkers(this, markers);
61
63
  }
62
- clearMarkers() {
63
- clearMarkers(this);
64
+ removeMarkers() {
65
+ removeMarkers(this);
64
66
  }
65
- openPopup(popup, id = getPopupId(popup)) {
66
- return openPopup(this, popup, id);
67
+ openPopup(popup) {
68
+ return openPopup(this, popup);
67
69
  }
68
70
  closePopup(id) {
69
71
  closePopupsById(this, id);
70
72
  }
71
- updatePopup(popup, id = getPopupId(popup)) {
72
- return updatePopup(this, popup, id);
73
+ updatePopup(popup) {
74
+ return updatePopup(this, popup);
75
+ }
76
+ removePopups() {
77
+ removePopups(this);
78
+ }
79
+ async export(options) {
80
+ return exportMap(this, options);
73
81
  }
74
82
  }
@@ -0,0 +1,4 @@
1
+ import type { MapkaMap } from "../map.js";
2
+ import type { MapkaExportOptions } from "../types/export.js";
3
+ export declare function exportMap(map: MapkaMap, options?: MapkaExportOptions): Promise<HTMLImageElement>;
4
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/modules/export.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,wBAAsB,SAAS,CAC7B,GAAG,EAAE,QAAQ,EACb,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CA+C3B"}
@@ -0,0 +1,40 @@
1
+ import { toPng } from "html-to-image";
2
+ export async function exportMap(map, options = {}) {
3
+ const { hideControls = false, hideMarkers = false, hidePopups = false, bbox } = options;
4
+ const container = map.getContainer();
5
+ const originalBounds = map.getBounds();
6
+ if (bbox) {
7
+ map.fitBounds(bbox, { padding: 20, animate: false });
8
+ }
9
+ return new Promise((resolve, reject) => {
10
+ map.once("render", () => {
11
+ toPng(container, {
12
+ skipFonts: navigator.userAgent.includes("Firefox"),
13
+ filter: (node) => {
14
+ if (node.classList) {
15
+ const isControlsVisibleOrNotControl = !hideControls || !node.classList.contains("maplibregl-control-container");
16
+ const isMarkersVisibleOrNotMarker = !hideMarkers || !node.classList.contains("maplibregl-marker");
17
+ const isPopupsVisibleOrNotPopup = !hidePopups || !node.classList.contains("maplibregl-popup");
18
+ return (isControlsVisibleOrNotControl &&
19
+ isMarkersVisibleOrNotMarker &&
20
+ isPopupsVisibleOrNotPopup);
21
+ }
22
+ return true;
23
+ },
24
+ })
25
+ .then((dataUrl) => {
26
+ const img = new Image();
27
+ img.src = dataUrl;
28
+ img.onload = () => resolve(img);
29
+ img.onerror = reject;
30
+ })
31
+ .catch(reject)
32
+ .finally(() => {
33
+ if (bbox) {
34
+ map.fitBounds(originalBounds, { animate: false });
35
+ }
36
+ });
37
+ });
38
+ map.triggerRepaint();
39
+ });
40
+ }
@@ -7,7 +7,7 @@ export declare function getMarkerId(marker: {
7
7
  export declare function addMarkers(currentMap: MapkaMap, markersOptions: MapkaMarkerOptions[]): void;
8
8
  export declare function removeMarkersByIds(map: MapkaMap, ids: string[]): void;
9
9
  export declare function updateMarkers(map: MapkaMap, markersOptions: MapkaMarkerOptions[]): void;
10
- export declare function clearMarkers(map: MapkaMap): void;
10
+ export declare function removeMarkers(map: MapkaMap): void;
11
11
  export declare function addStyleMarkers(map: MapkaMap): void;
12
12
  export declare function addStyleDiffMarkers(map: MapkaMap, next: StyleSpecification): void;
13
13
  //# sourceMappingURL=markers.d.ts.map
@@ -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,EAAqB,MAAM,oBAAoB,CAAC;AAkBhF,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAElD;AA4ED,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,YAAY,CAAC,GAAG,EAAE,QAAQ,QAKzC;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":"AAGA,OAAO,KAAK,EAAU,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAkBhF,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,UAElD;AA4ED,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"}
@@ -110,7 +110,7 @@ export function updateMarkers(map, markersOptions) {
110
110
  removeMarkersByIds(map, markersIds);
111
111
  addMarkers(map, markersOptions);
112
112
  }
113
- export function clearMarkers(map) {
113
+ export function removeMarkers(map) {
114
114
  for (const marker of map.markers) {
115
115
  marker.marker.remove();
116
116
  }
@@ -6,9 +6,10 @@ export declare function getPopupId(popup: {
6
6
  }): string;
7
7
  export declare function getOnClose(map: MapkaMap, id: string): () => void;
8
8
  export declare function enforceMaxPopups(map: MapkaMap): void;
9
- export declare function openPopup(map: MapkaMap, options: MapkaPopupOptions, id: string): string;
9
+ export declare function openPopup(map: MapkaMap, options: MapkaPopupOptions): string;
10
10
  export declare function updatePopupBaseOptions(popup: Popup, options: MapkaPopupOptions, newOptions: Omit<MapkaPopupOptions, "content">): Popup;
11
- export declare function updatePopup(map: MapkaMap, { content, ...newOptions }: MapkaPopupOptions, id: string): void;
11
+ export declare function updatePopup(map: MapkaMap, { content, ...newOptions }: MapkaPopupOptions): void;
12
12
  export declare function closeOnMapClickPopups(map: MapkaMap): void;
13
13
  export declare function closePopupsById(map: MapkaMap, id: string): void;
14
+ export declare function removePopups(map: MapkaMap): void;
14
15
  //# sourceMappingURL=popup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"popup.d.ts","sourceRoot":"","sources":["../../src/modules/popup.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,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,QAM7C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,UAyD9E;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,CACzB,GAAG,EAAE,QAAQ,EACb,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,EAAE,iBAAiB,EAC7C,EAAE,EAAE,MAAM,QA6BX;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,QAQlD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAMxD"}
1
+ {"version":3,"file":"popup.d.ts","sourceRoot":"","sources":["../../src/modules/popup.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,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,QAM7C;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,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,QAQlD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAMxD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,QAMzC"}
@@ -18,8 +18,8 @@ export function enforceMaxPopups(map) {
18
18
  popupToRemove?.container.remove();
19
19
  }
20
20
  }
21
- export function openPopup(map, options, id) {
22
- const { lngLat, content, closeButton, ...popupOptions } = options;
21
+ export function openPopup(map, options) {
22
+ const { lngLat, content, closeButton, id = getPopupId(options), ...popupOptions } = options;
23
23
  if (content instanceof HTMLElement) {
24
24
  const popup = new Popup({
25
25
  ...popupOptions,
@@ -65,7 +65,7 @@ export function openPopup(map, options, id) {
65
65
  return openPopup(map, {
66
66
  ...options,
67
67
  content: newContent,
68
- }, id);
68
+ });
69
69
  }
70
70
  throw new Error("Invalid popup content");
71
71
  }
@@ -82,7 +82,8 @@ export function updatePopupBaseOptions(popup, options, newOptions) {
82
82
  }
83
83
  return popup;
84
84
  }
85
- export function updatePopup(map, { content, ...newOptions }, id) {
85
+ export function updatePopup(map, { content, ...newOptions }) {
86
+ const id = getPopupId(newOptions);
86
87
  if (content instanceof HTMLElement) {
87
88
  const mapkaPopups = map.popups.filter((popup) => popup.id === id);
88
89
  for (const { popup, options } of mapkaPopups) {
@@ -105,7 +106,7 @@ export function updatePopup(map, { content, ...newOptions }, id) {
105
106
  return updatePopup(map, {
106
107
  ...newOptions,
107
108
  content: newContent,
108
- }, id);
109
+ });
109
110
  }
110
111
  }
111
112
  export function closeOnMapClickPopups(map) {
@@ -122,3 +123,10 @@ export function closePopupsById(map, id) {
122
123
  popup.container.remove();
123
124
  }
124
125
  }
126
+ export function removePopups(map) {
127
+ for (const popup of map.popups) {
128
+ popup.popup.remove();
129
+ popup.container.remove();
130
+ }
131
+ map.popups = [];
132
+ }
@@ -0,0 +1,7 @@
1
+ export type MapkaExportOptions = {
2
+ hideControls?: boolean;
3
+ hideMarkers?: boolean;
4
+ hidePopups?: boolean;
5
+ bbox?: [number, number, number, number];
6
+ };
7
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/types/export.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapka/maplibre-gl-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "Mapka JS SDK",
6
6
  "sideEffects": false,
@@ -24,6 +24,7 @@
24
24
  "license": "Apache-2.0",
25
25
  "dependencies": {
26
26
  "es-toolkit": "^1.43.0",
27
+ "html-to-image": "^1.11.13",
27
28
  "maplibre-gl": "^5.15.0",
28
29
  "preact": "^10.28.0"
29
30
  },
@@ -34,5 +35,5 @@
34
35
  "!**/__tests__/",
35
36
  "!lib/buildInfo.json"
36
37
  ],
37
- "gitHead": "0cab43f10e18aa9c0b063edc1612272477f09e80"
38
+ "gitHead": "912eb7ca07c1c2ab1e9eaadab20e2c1786c0e967"
38
39
  }
@@ -0,0 +1,25 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+
4
+ export const DownloadIcon = () => {
5
+ return (
6
+ <svg
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ width="24"
9
+ height="24"
10
+ viewBox="0 0 24 24"
11
+ fill="none"
12
+ stroke="currentColor"
13
+ stroke-width="2"
14
+ stroke-linecap="round"
15
+ stroke-linejoin="round"
16
+ aria-label="Download"
17
+ >
18
+ <title>Download</title>
19
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
20
+ <path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2" />
21
+ <path d="M7 11l5 5l5 -5" />
22
+ <path d="M12 4l0 12" />
23
+ </svg>
24
+ );
25
+ };
@@ -1,4 +1,5 @@
1
1
  /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+
2
3
  import { h, Fragment } from "preact";
3
4
  import { useState } from "preact/hooks";
4
5
  import type { MapkaPopupContent } from "../types/marker.js";
@@ -0,0 +1,29 @@
1
+ /** biome-ignore-all lint/correctness/noUnusedImports: <explanation> */
2
+ import { h } from "preact";
3
+
4
+ export const ProgressDownIcon = () => {
5
+ return (
6
+ <svg
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ width="24"
9
+ height="24"
10
+ viewBox="0 0 24 24"
11
+ fill="none"
12
+ stroke="currentColor"
13
+ stroke-width="2"
14
+ stroke-linecap="round"
15
+ stroke-linejoin="round"
16
+ class="icon icon-tabler icons-tabler-outline icon-tabler-progress-down"
17
+ >
18
+ <title>Progress Download</title>
19
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
20
+ <path d="M10 20.777a8.942 8.942 0 0 1 -2.48 -.969" />
21
+ <path d="M14 3.223a9.003 9.003 0 0 1 0 17.554" />
22
+ <path d="M4.579 17.093a8.961 8.961 0 0 1 -1.227 -2.592" />
23
+ <path d="M3.124 10.5c.16 -.95 .468 -1.85 .9 -2.675l.169 -.305" />
24
+ <path d="M6.907 4.579a8.954 8.954 0 0 1 3.093 -1.356" />
25
+ <path d="M12 9v6" />
26
+ <path d="M15 12l-3 3l-3 -3" />
27
+ </svg>
28
+ );
29
+ };
@@ -0,0 +1,111 @@
1
+ // biome-ignore lint/correctness/noUnusedImports: preact jsx
2
+ import { h } from "preact";
3
+ import { render } from "preact";
4
+ import { DownloadIcon } from "../components/DownloadIcon.js";
5
+ import { ProgressDownIcon } from "../components/ProgressDownIcon.js";
6
+ import type { IControl } from "maplibre-gl";
7
+ import type { MapkaMap } from "../map.js";
8
+ import type { MapkaExportOptions } from "../types/export.js";
9
+
10
+ export interface MapkaExportControlOptions extends MapkaExportOptions {
11
+ filename?: string;
12
+ }
13
+
14
+ const Button = ({ isExporting, onClick }: { isExporting: boolean; onClick: () => void }) => {
15
+ return (
16
+ <button
17
+ type="button"
18
+ className="maplibregl-ctrl maplibregl-ctrl-group"
19
+ title="Export map as PNG"
20
+ aria-label="Export map as PNG"
21
+ disabled={isExporting}
22
+ onClick={onClick}
23
+ >
24
+ {isExporting ? <ProgressDownIcon /> : <DownloadIcon />}
25
+ </button>
26
+ );
27
+ };
28
+
29
+ export class MapkaExportControl implements IControl {
30
+ private map: MapkaMap | undefined;
31
+ private container: HTMLDivElement | undefined;
32
+ private options: MapkaExportControlOptions;
33
+ private isExporting = false;
34
+
35
+ constructor(options: MapkaExportControlOptions = {}) {
36
+ this.options = {
37
+ filename: "map-export",
38
+ ...options,
39
+ };
40
+ }
41
+
42
+ private downloadImage(img: HTMLImageElement): void {
43
+ const link = document.createElement("a");
44
+ link.download = `${this.options.filename}.png`;
45
+ link.href = img.src;
46
+
47
+ link.click();
48
+ link.remove();
49
+ }
50
+
51
+ private handleError(error: Error) {
52
+ this.map?.logger.error(error, "Failed to export map image");
53
+ }
54
+
55
+ private onClick = async (): Promise<void> => {
56
+ if (!this.map || this.isExporting) return;
57
+
58
+ this.isExporting = true;
59
+ this.render();
60
+
61
+ this.map
62
+ .export({
63
+ hideControls: true,
64
+ hideMarkers: false,
65
+ hidePopups: false,
66
+ ...this.options,
67
+ })
68
+ .then((img) => this.downloadImage(img))
69
+ .catch((error) => this.handleError(error))
70
+ .finally(() => {
71
+ this.isExporting = false;
72
+ this.render();
73
+ });
74
+ };
75
+
76
+ private render(): void {
77
+ if (!this.container) {
78
+ this.map?.logger.error("Export control container not found for rendering");
79
+ return;
80
+ }
81
+ render(<Button isExporting={this.isExporting} onClick={this.onClick} />, this.container);
82
+ }
83
+
84
+ private unmount(): void {
85
+ if (!this.container) {
86
+ this.map?.logger.error("Export control container not found during unmount");
87
+ return;
88
+ }
89
+ render(null, this.container);
90
+ }
91
+
92
+ public onAdd(map: MapkaMap): HTMLElement {
93
+ this.map = map;
94
+
95
+ this.container = document.createElement("div");
96
+ this.container.className = "mapka-export-control";
97
+
98
+ this.render();
99
+
100
+ return this.container;
101
+ }
102
+
103
+ public onRemove(): void {
104
+ this.unmount();
105
+
106
+ this.container?.remove?.();
107
+
108
+ this.container = undefined;
109
+ this.map = undefined;
110
+ }
111
+ }