@sjvair/map-sdk 0.1.0-beta.1 → 0.1.0-beta.10

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 (52) hide show
  1. package/README.md +243 -17
  2. package/dist/core/create-map.svelte.d.ts +72 -5
  3. package/dist/core/create-map.svelte.js +229 -14
  4. package/dist/core/gl.d.ts +5 -0
  5. package/dist/core/interactions.svelte.d.ts +11 -3
  6. package/dist/core/interactions.svelte.js +128 -11
  7. package/dist/core/plugin-host.svelte.d.ts +30 -5
  8. package/dist/core/plugin-host.svelte.js +93 -14
  9. package/dist/core/plugin.d.ts +44 -2
  10. package/dist/core/shape-view.d.ts +27 -0
  11. package/dist/core/shape-view.js +323 -0
  12. package/dist/data/format.d.ts +1 -1
  13. package/dist/data/format.js +16 -3
  14. package/dist/data/messages/en.json +12 -2
  15. package/dist/data/selection.d.ts +6 -0
  16. package/dist/data/selection.js +21 -0
  17. package/dist/data/types.d.ts +5 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +1 -0
  20. package/dist/plugins/points.svelte.d.ts +14 -2
  21. package/dist/plugins/points.svelte.js +34 -5
  22. package/dist/plugins/shapes.svelte.d.ts +5 -0
  23. package/dist/plugins/shapes.svelte.js +20 -2
  24. package/dist/presets/FireTooltip.svelte +70 -0
  25. package/dist/presets/FireTooltip.svelte.d.ts +13 -0
  26. package/dist/presets/MonitorClusterTooltip.svelte +21 -2
  27. package/dist/presets/cluster-area.d.ts +13 -0
  28. package/dist/presets/cluster-area.js +34 -0
  29. package/dist/presets/hms-fire.d.ts +11 -7
  30. package/dist/presets/hms-fire.js +16 -17
  31. package/dist/presets/hms-smoke.d.ts +2 -1
  32. package/dist/presets/hms-smoke.js +4 -2
  33. package/dist/stores/hms-stores.svelte.d.ts +6 -3
  34. package/dist/stores/hms-stores.svelte.js +4 -4
  35. package/dist/ui/FeatureList.svelte +17 -17
  36. package/dist/ui/Legend.svelte +58 -16
  37. package/dist/ui/Legend.svelte.d.ts +9 -0
  38. package/dist/ui/MapTooltip.svelte +253 -14
  39. package/dist/ui/MapTooltip.svelte.d.ts +9 -0
  40. package/dist/ui/PickNotice.svelte +56 -0
  41. package/dist/ui/PickNotice.svelte.d.ts +7 -0
  42. package/dist/ui/TooltipItem.svelte +12 -12
  43. package/dist/ui/card.d.ts +29 -0
  44. package/dist/ui/card.js +1 -0
  45. package/dist/ui/feature-list.d.ts +17 -2
  46. package/dist/ui/feature-list.js +45 -10
  47. package/dist/ui/value-format.d.ts +3 -2
  48. package/dist/ui/value-format.js +9 -2
  49. package/dist/view/MapView.svelte +72 -2
  50. package/dist/view/MapView.svelte.d.ts +8 -0
  51. package/dist/view/create-gl-map.js +4 -0
  52. package/package.json +2 -1
@@ -11,8 +11,10 @@
11
11
  import type { SortBy } from "../ui/feature-list.js";
12
12
  import MapControls from "../ui/MapControls.svelte";
13
13
  import MapTooltip from "../ui/MapTooltip.svelte";
14
+ import type { CardOptions } from "../ui/card.js";
14
15
  import SelectionBar from "../ui/SelectionBar.svelte";
15
16
  import ViewSummary from "../ui/ViewSummary.svelte";
17
+ import PickNotice from "../ui/PickNotice.svelte";
16
18
  import { createDataCredits } from "../ui/data-credits.svelte.js";
17
19
  import { createGlMap, setDataCredits, watchTheme } from "./create-gl-map.js";
18
20
  import {
@@ -56,6 +58,13 @@
56
58
  * Keyboard focus from the feature list always shows it pinned.
57
59
  */
58
60
  tooltip?: "pinned" | "pointer";
61
+ /**
62
+ * `true`: a click or tap on a feature pins its reading card, which takes the pointer and
63
+ * closes with Esc, its Close button, or a click elsewhere on the map; focusing a
64
+ * feature-list row shows the same card. Selection and `featureclick` are unchanged.
65
+ * `{ actions }` also puts the host's buttons on the card (see `CardContext`).
66
+ */
67
+ card?: boolean | CardOptions;
59
68
  }
60
69
 
61
70
  let {
@@ -67,9 +76,50 @@
67
76
  featureList = true,
68
77
  selectionBar = true,
69
78
  controls = "mapsdk",
70
- tooltip = "pinned"
79
+ tooltip = "pinned",
80
+ card = false
71
81
  }: Props = $props();
72
82
 
83
+ // Pinning only, no plugin setup, so it's safe in an effect.
84
+ // Boolean(): an untyped host's `null` is off, like `false`.
85
+ const cardOn = $derived(Boolean(card));
86
+ const cardActions = $derived(card && typeof card === "object" ? card.actions : undefined);
87
+
88
+ $effect(() => {
89
+ map.setCardEnabled(cardOn);
90
+ return () => map.setCardEnabled(false);
91
+ });
92
+
93
+ /**
94
+ * Esc outside the card closes it when focus is in this map or nowhere (the body), so a key
95
+ * pressed in another map, or elsewhere on the page, leaves this card alone.
96
+ */
97
+ function onWindowKeydown(e: KeyboardEvent) {
98
+ if (e.key !== "Escape" || e.defaultPrevented || !map.pinned) return;
99
+ const active = document.activeElement;
100
+ if (active && active !== document.body && !root.contains(active)) return;
101
+ // Focus nowhere: only the most recently pinned card on the page closes.
102
+ if (!active || active === document.body) {
103
+ let latest: HTMLElement | null = null;
104
+ for (const el of document.querySelectorAll<HTMLElement>("[data-mapsdk-pinned-at]")) {
105
+ if (!latest || Number(el.dataset.mapsdkPinnedAt) > Number(latest.dataset.mapsdkPinnedAt)) {
106
+ latest = el;
107
+ }
108
+ }
109
+ if (latest && latest !== root) return;
110
+ }
111
+ e.preventDefault();
112
+ map.unpin();
113
+ }
114
+
115
+ // When this map's card was pinned, so Esc with focus nowhere closes the latest card only.
116
+ $effect(() => {
117
+ const pinned = map.pinned;
118
+ if (!root) return;
119
+ if (pinned) root.dataset.mapsdkPinnedAt = String(performance.now());
120
+ else delete root.dataset.mapsdkPinnedAt;
121
+ });
122
+
73
123
  const list = $derived(resolveFeatureList(featureList));
74
124
  const sides = $derived(list ? cornerSides(list.position) : null);
75
125
 
@@ -148,6 +198,12 @@
148
198
  function onListKeydown(e: KeyboardEvent) {
149
199
  if (e.key !== "Escape") return;
150
200
  e.preventDefault();
201
+ // The card closes first (and the focused row's card with it); the next Esc closes the panel.
202
+ if (cardOn && (map.pinned || map.focusedKey)) {
203
+ map.unpin();
204
+ map.focusFeature(null);
205
+ return;
206
+ }
151
207
  listOpen = false;
152
208
  listButton?.focus();
153
209
  }
@@ -242,6 +298,8 @@
242
298
  });
243
299
  </script>
244
300
 
301
+ <svelte:window onkeydown={onWindowKeydown} />
302
+
245
303
  <div
246
304
  bind:this={root}
247
305
  bind:clientWidth={width}
@@ -250,9 +308,9 @@
250
308
  role="region"
251
309
  aria-label={label ?? map.messages("mapsdk_map_label")}
252
310
  data-mapsdk-map={map.uid}
311
+ style:--mapsdk-map-width={width > 0 ? `${width}px` : undefined}
253
312
  >
254
313
  <div class="mapsdk-gl" bind:this={glContainer}></div>
255
- <MapTooltip {map} {project} {width} {height} mode={tooltip} gl={projector} />
256
314
  {#if list && sides}
257
315
  <div
258
316
  class="mapsdk-list-panel"
@@ -282,11 +340,23 @@
282
340
  {/if}
283
341
  </div>
284
342
  {/if}
343
+ <!-- After the list, so Tab from a list row reaches the pinned card's Close button. -->
344
+ <MapTooltip
345
+ {map}
346
+ {project}
347
+ {width}
348
+ {height}
349
+ mode={tooltip}
350
+ gl={projector}
351
+ card={cardOn}
352
+ actions={cardActions}
353
+ />
285
354
  <!-- After the list, so Tab reaches the List button first. -->
286
355
  {#if selectionBar}
287
356
  <SelectionBar {map} clearRight={list?.position === "top-right" && controls === "none"} />
288
357
  {/if}
289
358
  <ViewSummary {map} />
359
+ <PickNotice {map} />
290
360
  {#if controls === "mapsdk"}
291
361
  <MapControls {map} gl={projector} />
292
362
  {/if}
@@ -2,6 +2,7 @@ import "@maptiler/sdk/dist/maptiler-sdk.css";
2
2
  import "../ui/controls.css";
3
3
  import type { Snippet } from "svelte";
4
4
  import type { MapContext } from "../core/create-map.svelte.js";
5
+ import type { CardOptions } from "../ui/card.js";
5
6
  import { type FeatureListOptions } from "./list-placement.js";
6
7
  interface Props {
7
8
  /** The map to show. Fixed for the component's life. */
@@ -37,6 +38,13 @@ interface Props {
37
38
  * Keyboard focus from the feature list always shows it pinned.
38
39
  */
39
40
  tooltip?: "pinned" | "pointer";
41
+ /**
42
+ * `true`: a click or tap on a feature pins its reading card, which takes the pointer and
43
+ * closes with Esc, its Close button, or a click elsewhere on the map; focusing a
44
+ * feature-list row shows the same card. Selection and `featureclick` are unchanged.
45
+ * `{ actions }` also puts the host's buttons on the card (see `CardContext`).
46
+ */
47
+ card?: boolean | CardOptions;
40
48
  }
41
49
  declare const MapView: import("svelte").Component<Props, {}, "">;
42
50
  type MapView = ReturnType<typeof MapView>;
@@ -128,6 +128,10 @@ function recordView(gl, ctx) {
128
128
  */
129
129
  export function createGlMap(container, ctx, engineControls = false) {
130
130
  const { camera, style } = untrack(() => ({ camera: ctx.camera, style: styleOf(ctx) }));
131
+ // Before the map exists, so its telemetry timer finds it off. Sessions stay on: MapTiler
132
+ // bills SDK maps by session, and without one every tile request counts on its own.
133
+ if (ctx.config.telemetry === false)
134
+ maptilerConfig.telemetry = false;
131
135
  const gl = new GlMap({
132
136
  container,
133
137
  style,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sjvair/map-sdk",
3
3
  "description": "Svelte 5 map components for SJVAir air-quality data",
4
- "version": "0.1.0-beta.1",
4
+ "version": "0.1.0-beta.10",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -63,6 +63,7 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@maptiler/sdk": "~4.2.0",
66
+ "@turf/area": "^7.4.0",
66
67
  "@turf/clusters-dbscan": "^7.4.0",
67
68
  "@turf/helpers": "^7.4.0",
68
69
  "@types/geojson": "^7946.0.16",