solid-map-gl 2.0.1 → 2.2.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.
package/dist/index.jsx CHANGED
@@ -1,16 +1,23 @@
1
1
  // src/components/MapGL/index.tsx
2
2
  import {
3
3
  createSignal,
4
- createEffect,
4
+ createEffect as createEffect2,
5
5
  onMount,
6
6
  onCleanup,
7
7
  on
8
8
  } from "solid-js";
9
+ import { isServer } from "solid-js/web";
9
10
 
10
11
  // src/components/MapProvider/index.tsx
11
- import { createContext, useContext } from "solid-js";
12
+ import { createContext, useContext, createEffect } from "solid-js";
12
13
  import { createStore } from "solid-js/store";
13
- var defaultState = { map: null, mapLib: null, isMapLibre: false };
14
+ var defaultState = {
15
+ map: null,
16
+ mapLib: null,
17
+ isMapLibre: false,
18
+ constants: {},
19
+ themeVersion: 0
20
+ };
14
21
  var MapContext = createContext([defaultState]);
15
22
  var useMapContext = () => useContext(MapContext);
16
23
  var MapProvider = (props) => {
@@ -18,6 +25,10 @@ var MapProvider = (props) => {
18
25
  props.map && setState("map", props.map);
19
26
  props.mapLib && setState("mapLib", props.mapLib);
20
27
  setState("isMapLibre", !!props.isMapLibre);
28
+ setState("constants", props.constants || {});
29
+ setState("themeVersion", props.themeVersion || 0);
30
+ createEffect(() => setState("constants", props.constants || {}));
31
+ createEffect(() => setState("themeVersion", props.themeVersion || 0));
21
32
  return <MapContext.Provider value={[state]}>{props.children}</MapContext.Provider>;
22
33
  };
23
34
 
@@ -218,6 +229,79 @@ var rasterStyleList = {
218
229
  }
219
230
  };
220
231
 
232
+ // src/tilesSettled.ts
233
+ var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
234
+ var hasActiveFadeTransition = (map) => {
235
+ try {
236
+ return !!map.style?.hasTransitions?.();
237
+ } catch {
238
+ return false;
239
+ }
240
+ };
241
+ function pollTilesLoaded(map, timeout) {
242
+ return new Promise((resolve) => {
243
+ const deadline = Date.now() + timeout;
244
+ const poll = () => {
245
+ if (map.areTilesLoaded()) {
246
+ resolve(true);
247
+ return;
248
+ }
249
+ if (Date.now() >= deadline) {
250
+ resolve(false);
251
+ return;
252
+ }
253
+ setTimeout(poll, 100);
254
+ };
255
+ poll();
256
+ });
257
+ }
258
+ async function settleAfterIdle(map, options = {}) {
259
+ const { timeout = 1e4, fadeMargin = 400 } = options;
260
+ await pollTilesLoaded(map, timeout);
261
+ map.triggerRepaint();
262
+ await nextFrame();
263
+ await nextFrame();
264
+ if (fadeMargin > 0) {
265
+ const fadeDeadline = Date.now() + Math.max(fadeMargin, 2e3);
266
+ while (hasActiveFadeTransition(map) && Date.now() < fadeDeadline) {
267
+ map.triggerRepaint();
268
+ await nextFrame();
269
+ }
270
+ await new Promise((resolve) => setTimeout(resolve, fadeMargin));
271
+ map.triggerRepaint();
272
+ await nextFrame();
273
+ await nextFrame();
274
+ }
275
+ }
276
+ function waitForIdleAndSettle(map, options = {}) {
277
+ return new Promise((resolve) => {
278
+ map.once("idle", () => {
279
+ settleAfterIdle(map, options).then(resolve);
280
+ });
281
+ });
282
+ }
283
+ function disableRasterFade(map) {
284
+ for (const layer of map.getStyle()?.layers ?? []) {
285
+ try {
286
+ map.setPaintProperty(layer.id, "raster-fade-duration", 0);
287
+ } catch {
288
+ }
289
+ }
290
+ }
291
+
292
+ // src/offscreenCapture.ts
293
+ function createCapturer(map, settleOptions) {
294
+ return {
295
+ map,
296
+ waitUntilSettled: () => waitForIdleAndSettle(map, settleOptions),
297
+ capture: (type = "image/jpeg", quality = 0.92) => map.getCanvas().toDataURL(type, quality),
298
+ captureWhenSettled: async (type = "image/jpeg", quality = 0.92) => {
299
+ await waitForIdleAndSettle(map, settleOptions);
300
+ return map.getCanvas().toDataURL(type, quality);
301
+ }
302
+ };
303
+ }
304
+
221
305
  // src/components/MapGL/index.tsx
222
306
  var MapGL = (props) => {
223
307
  let map;
@@ -226,9 +310,11 @@ var MapGL = (props) => {
226
310
  let mutationObserver;
227
311
  let mapLib;
228
312
  let isMapLibre = false;
313
+ let tilesLoadedGeneration = 0;
229
314
  const [mapLoaded, setMapLoaded] = createSignal(null);
315
+ const [themeVersion, setThemeVersion] = createSignal(0);
230
316
  const [darkMode, setDarkMode] = createSignal(
231
- typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches || typeof document !== "undefined" && document.body.classList.contains("dark")
317
+ !isServer && (window.matchMedia("(prefers-color-scheme: dark)").matches || document.body.classList.contains("dark"))
232
318
  );
233
319
  let interruptingEase = false;
234
320
  let expectingOwnEcho = false;
@@ -257,7 +343,11 @@ var MapGL = (props) => {
257
343
  projection: props.options?.projection,
258
344
  container: mapRef,
259
345
  style: getStyle(props.options?.style, props.darkStyle),
260
- fitBoundsOptions: { padding: props.viewport?.padding }
346
+ fitBoundsOptions: { padding: props.viewport?.padding },
347
+ // Required for capture()/toDataURL() to read back anything from the canvas at all — forced
348
+ // regardless of `options` since getting this wrong doesn't degrade capture, it silently
349
+ // breaks it (an all-transparent/blank image, no error).
350
+ ...props.offscreen ? { preserveDrawingBuffer: true } : {}
261
351
  });
262
352
  map.debug = props.debug;
263
353
  map.debugEvents = props.debugEvents;
@@ -289,9 +379,30 @@ var MapGL = (props) => {
289
379
  }
290
380
  }
291
381
  });
382
+ if (props.onTilesLoaded) {
383
+ const timeout = props.tilesLoadedTimeout ?? 1e4;
384
+ const fadeMargin = props.tilesLoadedFadeMargin ?? 400;
385
+ map.on("idle", () => {
386
+ const generation = ++tilesLoadedGeneration;
387
+ settleAfterIdle(map, { timeout, fadeMargin }).then(() => {
388
+ if (generation !== tilesLoadedGeneration) return;
389
+ debug("Tiles settled");
390
+ props.onTilesLoaded();
391
+ });
392
+ });
393
+ }
292
394
  map.once("load", () => {
293
395
  setMapLoaded(map);
294
396
  debug("Map loaded");
397
+ if (props.offscreen) {
398
+ if (props.offscreen.disableRasterFade !== false) disableRasterFade(map);
399
+ props.onCapturerReady?.(
400
+ createCapturer(map, {
401
+ timeout: props.tilesLoadedTimeout ?? 1e4,
402
+ fadeMargin: props.tilesLoadedFadeMargin ?? 400
403
+ })
404
+ );
405
+ }
295
406
  ["mousedown", "touchstart", "wheel"].forEach(
296
407
  (event) => map.on(
297
408
  event,
@@ -304,16 +415,19 @@ var MapGL = (props) => {
304
415
  (evt) => !evt.rotate && props.onUserInteraction?.(false)
305
416
  )
306
417
  );
307
- const darkTheme = typeof window !== "undefined" && window?.matchMedia("(prefers-color-scheme: dark)");
418
+ const darkTheme = !isServer && window.matchMedia("(prefers-color-scheme: dark)");
308
419
  darkTheme?.addEventListener("change", () => {
309
420
  setDarkMode(darkTheme.matches);
421
+ setThemeVersion((v) => v + 1);
310
422
  debug("Set dark theme to:", darkTheme.matches?.toString());
311
423
  });
312
424
  mutationObserver = new MutationObserver(() => {
313
- const darkTheme2 = document.body.classList.contains("dark");
425
+ const darkTheme2 = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark");
314
426
  setDarkMode(darkTheme2);
427
+ setThemeVersion((v) => v + 1);
315
428
  debug("Set theme to:", darkTheme2);
316
429
  });
430
+ mutationObserver.observe(document.documentElement, { attributes: true });
317
431
  mutationObserver.observe(document.body, { attributes: true });
318
432
  if (!props.disableResize) {
319
433
  resizeObserver = new ResizeObserver(() => {
@@ -368,7 +482,7 @@ var MapGL = (props) => {
368
482
  });
369
483
  });
370
484
  });
371
- createEffect(
485
+ createEffect2(
372
486
  on(
373
487
  () => props.viewport,
374
488
  (vp) => {
@@ -397,7 +511,7 @@ var MapGL = (props) => {
397
511
  { defer: true }
398
512
  )
399
513
  );
400
- createEffect(() => {
514
+ createEffect2(() => {
401
515
  const loadedMap = mapLoaded();
402
516
  if (typeof loadedMap?.setConfigProperty !== "function") {
403
517
  if (loadedMap && props.config && Object.keys(props.config).length) {
@@ -415,13 +529,13 @@ var MapGL = (props) => {
415
529
  debug(`Set Config (${id}:${key}) to:`, value);
416
530
  }
417
531
  });
418
- createEffect(() => {
532
+ createEffect2(() => {
419
533
  const proj = props.options?.projection;
420
534
  if (!map || !proj) return;
421
535
  map.setProjection(proj);
422
536
  debug("Set Projection to:", proj);
423
537
  });
424
- createEffect(() => {
538
+ createEffect2(() => {
425
539
  const cur = props.cursorStyle;
426
540
  if (!map || !cur) return;
427
541
  map.getCanvas().style.cursor = cur;
@@ -436,7 +550,7 @@ var MapGL = (props) => {
436
550
  });
437
551
  return list;
438
552
  };
439
- createEffect((prev) => {
553
+ createEffect2((prev) => {
440
554
  const style = getStyle(props.options?.style, props.darkStyle);
441
555
  if (map?.isStyleLoaded() && prev !== style) {
442
556
  const oldStyle = map.getStyle();
@@ -469,7 +583,7 @@ var MapGL = (props) => {
469
583
  "showPadding",
470
584
  "showOverdrawInspector"
471
585
  ].forEach((item) => {
472
- createEffect(() => {
586
+ createEffect2(() => {
473
587
  const prop = props[item];
474
588
  if (!map || !prop) return;
475
589
  map[item] = prop;
@@ -479,6 +593,7 @@ var MapGL = (props) => {
479
593
  onCleanup(() => {
480
594
  resizeObserver?.disconnect();
481
595
  mutationObserver?.disconnect();
596
+ tilesLoadedGeneration++;
482
597
  map?.remove();
483
598
  debug("Map removed");
484
599
  });
@@ -487,9 +602,22 @@ var MapGL = (props) => {
487
602
  id={props.id}
488
603
  class={props?.class}
489
604
  classList={props?.classList}
490
- style={{ width: "100%", height: "100%", ...props.style }}
605
+ style={props.offscreen ? {
606
+ position: "fixed",
607
+ left: "-99999px",
608
+ top: "-99999px",
609
+ width: `${props.offscreen.width}px`,
610
+ height: `${props.offscreen.height}px`,
611
+ ...props.style
612
+ } : { width: "100%", height: "100%", ...props.style }}
613
+ >
614
+ {mapLoaded() && <MapProvider
615
+ map={mapLoaded()}
616
+ mapLib={mapLib}
617
+ isMapLibre={isMapLibre}
618
+ constants={props.constants}
619
+ themeVersion={themeVersion()}
491
620
  >
492
- {mapLoaded() && <MapProvider map={mapLoaded()} mapLib={mapLib} isMapLibre={isMapLibre}>
493
621
  <style>{`.overlay{position:relative;width:100%;height:100%;pointer-events:none}:where(.overlay>*){pointer-events:auto}`}</style>
494
622
  <div class="overlay">{props.children}</div>
495
623
  </MapProvider>}
@@ -499,7 +627,7 @@ var MapGL = (props) => {
499
627
  // src/components/Source/index.tsx
500
628
  import {
501
629
  onCleanup as onCleanup2,
502
- createEffect as createEffect2,
630
+ createEffect as createEffect3,
503
631
  createContext as createContext2,
504
632
  useContext as useContext2,
505
633
  createUniqueId
@@ -537,14 +665,14 @@ var Source = (props) => {
537
665
  debug("Add Source:", props.id);
538
666
  switch (anySource().type) {
539
667
  case "geojson":
540
- createEffect2(() => {
668
+ createEffect3(() => {
541
669
  const data = anySource().data;
542
670
  ctx.map.getSource(props.id).setData(data || {});
543
671
  debug("Update GeoJSON Data:", props.id);
544
672
  });
545
673
  break;
546
674
  case "image":
547
- createEffect2(() => {
675
+ createEffect3(() => {
548
676
  const url = anySource().url;
549
677
  const coords = anySource().coordinates;
550
678
  ctx.map.getSource(props.id).updateImage(url, coords);
@@ -552,7 +680,7 @@ var Source = (props) => {
552
680
  });
553
681
  break;
554
682
  case "vector":
555
- createEffect2(() => {
683
+ createEffect3(() => {
556
684
  const url = anySource().url;
557
685
  const tiles = anySource().tiles;
558
686
  const source = ctx.map.getSource(props.id);
@@ -561,7 +689,7 @@ var Source = (props) => {
561
689
  });
562
690
  break;
563
691
  case "raster":
564
- createEffect2(() => {
692
+ createEffect3(() => {
565
693
  const src = lookup(anySource().url);
566
694
  const source = ctx.map.getSource(props.id);
567
695
  src.url ? source.setUrl(src.url) : source.setTiles(src.tiles);
@@ -584,7 +712,7 @@ var Source = (props) => {
584
712
  // src/components/Layer/index.tsx
585
713
  import {
586
714
  onCleanup as onCleanup3,
587
- createEffect as createEffect3,
715
+ createEffect as createEffect4,
588
716
  createMemo,
589
717
  createUniqueId as createUniqueId2
590
718
  } from "solid-js";
@@ -653,7 +781,70 @@ var layoutStyles = [
653
781
  "text-writing-mode"
654
782
  ];
655
783
 
784
+ // src/colors.ts
785
+ var probe;
786
+ var toRgbString = (cssColor) => {
787
+ probe ??= document.body.appendChild(document.createElement("div"));
788
+ probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none";
789
+ probe.style.color = "";
790
+ probe.style.color = cssColor;
791
+ return probe.style.color ? getComputedStyle(probe).color : void 0;
792
+ };
793
+ var tailwindName = /^[a-z]+-\d{2,3}$/;
794
+ var nativelySupported = /^(rgb|rgba|hsl|hsla)\(/i;
795
+ var cssColorFunction = /^[a-z-]+\(/i;
796
+ var classProbe;
797
+ var bgClassPair = /^bg-[a-z]+-\d{2,3}(?:\s+dark:bg-[a-z]+-\d{2,3})?$/;
798
+ var resolveClassPair = (value) => {
799
+ classProbe ??= document.body.appendChild(document.createElement("div"));
800
+ classProbe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none";
801
+ classProbe.className = value;
802
+ const bg = getComputedStyle(classProbe).backgroundColor;
803
+ classProbe.className = "";
804
+ return bg && bg !== "rgba(0, 0, 0, 0)" ? bg : value;
805
+ };
806
+ var resolveColor = (value) => {
807
+ if (bgClassPair.test(value)) return resolveClassPair(value);
808
+ if (tailwindName.test(value)) {
809
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(`--color-${value}`).trim();
810
+ return raw && toRgbString(raw) || value;
811
+ }
812
+ if (cssColorFunction.test(value) && !nativelySupported.test(value)) {
813
+ return toRgbString(value) || value;
814
+ }
815
+ return value;
816
+ };
817
+ var toRgbaComponents = (value) => {
818
+ const rgb = toRgbString(resolveColor(value)) ?? value;
819
+ const match = rgb.match(/rgba?\(([^)]+)\)/i);
820
+ if (!match) return [0, 0, 0, 1];
821
+ const [r, g, b, a = 1] = match[1].split(",").map(Number);
822
+ return [r, g, b, a];
823
+ };
824
+
656
825
  // src/components/Layer/index.tsx
826
+ var PULSE_DEFAULTS = {
827
+ property: "icon-halo-width",
828
+ from: 0,
829
+ to: 8,
830
+ duration: 1500,
831
+ waveform: "out"
832
+ };
833
+ var PULSE_RAMP_FRACTION = 0.75;
834
+ var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
835
+ var pulseShape = (elapsed, duration, waveform) => {
836
+ const phase = elapsed % duration / duration;
837
+ if (waveform === "in-out") return (1 - Math.cos(phase * Math.PI * 2)) / 2;
838
+ const ramp = Math.min(phase / PULSE_RAMP_FRACTION, 1);
839
+ return easeOutQuad(ramp);
840
+ };
841
+ var lerpColor = (from, to, t) => {
842
+ const r = Math.round(from[0] + (to[0] - from[0]) * t);
843
+ const g = Math.round(from[1] + (to[1] - from[1]) * t);
844
+ const b = Math.round(from[2] + (to[2] - from[2]) * t);
845
+ const a = from[3] + (to[3] - from[3]) * t;
846
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
847
+ };
657
848
  var diff = (newProps = {}, prevProps = {}) => {
658
849
  const keys = /* @__PURE__ */ new Set([...Object.keys(newProps), ...Object.keys(prevProps)]);
659
850
  return [...keys].reduce((acc, key) => {
@@ -665,26 +856,43 @@ var diff = (newProps = {}, prevProps = {}) => {
665
856
  }, []);
666
857
  };
667
858
  var newKey = (key, type) => (key.startsWith(type) || key.startsWith("icon") || key.startsWith("text") ? "" : type + "-") + key.replace(/[A-Z]/g, (s) => "-" + s.toLowerCase());
668
- var updateStyle = (oldStyle) => {
859
+ var resolveColor2 = (key, value) => key.endsWith("color") && typeof value === "string" ? resolveColor(value) : value;
860
+ var constantRef = /^@(.+)$/;
861
+ var resolveConstant = (key, value, constants, debug) => {
862
+ if (typeof value !== "string") return value;
863
+ const match = value.match(constantRef);
864
+ if (!match) return value;
865
+ const resolved = constants[match[1]];
866
+ if (resolved === void 0) {
867
+ debug(`Unknown constant "@${match[1]}" referenced by "${key}" \u2014 leaving unresolved`);
868
+ return value;
869
+ }
870
+ return resolved;
871
+ };
872
+ var updateStyle = (oldStyle, constants = {}, debug = () => {
873
+ }) => {
669
874
  if (!oldStyle) return;
670
875
  let layout = {};
671
876
  let paint = {};
672
877
  let style = {};
878
+ const resolvePaint = (key, value) => resolveColor2(key, resolveConstant(key, value, constants, debug));
673
879
  Object.entries(oldStyle).forEach(([key, value]) => {
674
880
  if (baseStyle.includes(key)) style[key] = value;
675
881
  else {
676
882
  const nk = newKey(key, oldStyle.type);
677
- layoutStyles.includes(nk) ? layout[nk] = value : paint[nk] = value;
883
+ layoutStyles.includes(nk) ? layout[nk] = resolveConstant(nk, value, constants, debug) : paint[nk] = resolvePaint(nk, value);
678
884
  }
679
885
  });
680
886
  if (oldStyle.paint)
681
- Object.entries(oldStyle.paint).forEach(
682
- ([key, value]) => paint[newKey(key, oldStyle.type)] = value
683
- );
887
+ Object.entries(oldStyle.paint).forEach(([key, value]) => {
888
+ const nk = newKey(key, oldStyle.type);
889
+ paint[nk] = resolvePaint(nk, value);
890
+ });
684
891
  if (oldStyle.layout)
685
- Object.entries(oldStyle.layout).forEach(
686
- ([key, value]) => layout[newKey(key, oldStyle.type)] = value
687
- );
892
+ Object.entries(oldStyle.layout).forEach(([key, value]) => {
893
+ const nk = newKey(key, oldStyle.type);
894
+ layout[nk] = resolveConstant(nk, value, constants, debug);
895
+ });
688
896
  return { ...style, paint, layout };
689
897
  };
690
898
  var Layer = (props) => {
@@ -699,7 +907,7 @@ var Layer = (props) => {
699
907
  );
700
908
  ctx.map.addLayer(
701
909
  props.customLayer || {
702
- ...updateStyle(props.style),
910
+ ...updateStyle(props.style, ctx.constants, debug),
703
911
  id: layerId,
704
912
  source: sourceId,
705
913
  // `slot` is Mapbox Standard-Style-only — MapLibre has no equivalent (see docs/dev-notes.md)
@@ -723,8 +931,9 @@ var Layer = (props) => {
723
931
  });
724
932
  }
725
933
  });
726
- createEffect3((prev) => {
727
- const style = updateStyle(props.style);
934
+ createEffect4((prev) => {
935
+ ctx.themeVersion;
936
+ const style = updateStyle(props.style, ctx.constants, debug);
728
937
  if (style === prev) return;
729
938
  if (style.layout !== prev?.layout)
730
939
  diff(style.layout, prev?.layout).forEach(
@@ -740,8 +949,8 @@ var Layer = (props) => {
740
949
  ctx.map.setFilter(layerId, style.filter, { validate: false });
741
950
  debug("Update Layer Style:", layerId);
742
951
  return style;
743
- }, updateStyle(props.style));
744
- createEffect3((prev) => {
952
+ }, updateStyle(props.style, ctx.constants, debug));
953
+ createEffect4((prev) => {
745
954
  if (props.visible === prev) return;
746
955
  ctx.map.setLayoutProperty(
747
956
  layerId,
@@ -752,19 +961,19 @@ var Layer = (props) => {
752
961
  debug(`Update Visibility (${layerId}):`, props.visible.toString());
753
962
  return props.visible;
754
963
  }, props.visible);
755
- createEffect3((prev) => {
964
+ createEffect4((prev) => {
756
965
  if (getBeforeId() === prev) return prev;
757
966
  ctx.map.moveLayer(layerId, getBeforeId());
758
967
  debug(`Update Layer Z-Index (${layerId}):`, getBeforeId());
759
968
  return getBeforeId();
760
969
  }, getBeforeId());
761
- createEffect3(async () => {
970
+ createEffect4(async () => {
762
971
  if (!props.filter) return;
763
972
  !ctx.map.isStyleLoaded() && await ctx.map.once("styledata");
764
973
  ctx.map.setFilter(layerId, props.filter);
765
974
  debug(`Update Filter (${layerId}):`, props.filter);
766
975
  });
767
- createEffect3(async () => {
976
+ createEffect4(async () => {
768
977
  if (!props.featureState || props.featureState.id === null) return;
769
978
  !ctx.map.isStyleLoaded() && await ctx.map.once("styledata");
770
979
  ctx.map.removeFeatureState({
@@ -780,6 +989,44 @@ var Layer = (props) => {
780
989
  props.featureState.state
781
990
  );
782
991
  });
992
+ createEffect4(() => {
993
+ if (!props.pulse) return;
994
+ const configs = props.pulse === true ? [{}] : Array.isArray(props.pulse) ? props.pulse : [props.pulse];
995
+ const resolved = configs.map((config) => {
996
+ const { property, from, to, duration, waveform } = {
997
+ ...PULSE_DEFAULTS,
998
+ ...config
999
+ };
1000
+ const isColor = typeof from === "string" || typeof to === "string";
1001
+ const [a, b] = waveform === "in" ? [to, from] : [from, to];
1002
+ return {
1003
+ property,
1004
+ duration,
1005
+ waveform,
1006
+ isColor,
1007
+ fromNum: isColor ? 0 : a,
1008
+ toNum: isColor ? 0 : b,
1009
+ fromRgba: isColor ? toRgbaComponents(a) : null,
1010
+ toRgba: isColor ? toRgbaComponents(b) : null
1011
+ };
1012
+ });
1013
+ const start = performance.now();
1014
+ let frameId;
1015
+ const loop = (now) => {
1016
+ const elapsed = now - start;
1017
+ resolved.forEach((cfg) => {
1018
+ const t = pulseShape(elapsed, cfg.duration, cfg.waveform);
1019
+ const value = cfg.isColor ? lerpColor(cfg.fromRgba, cfg.toRgba, t) : cfg.fromNum + (cfg.toNum - cfg.fromNum) * t;
1020
+ ctx.map.setPaintProperty(layerId, cfg.property, value, {
1021
+ validate: false
1022
+ });
1023
+ });
1024
+ frameId = window.requestAnimationFrame(loop);
1025
+ };
1026
+ frameId = window.requestAnimationFrame(loop);
1027
+ debug(`Start Pulse (${layerId}):`, props.pulse);
1028
+ onCleanup3(() => window.cancelAnimationFrame(frameId));
1029
+ });
783
1030
  onCleanup3(() => ctx.map?.getLayer(layerId) && ctx.map?.removeLayer(layerId));
784
1031
  return props.children;
785
1032
  };
@@ -934,10 +1181,10 @@ var Layer3D = (props) => {
934
1181
  };
935
1182
 
936
1183
  // src/components/Atmosphere/index.tsx
937
- import { onCleanup as onCleanup5, createEffect as createEffect4 } from "solid-js";
1184
+ import { onCleanup as onCleanup5, createEffect as createEffect5 } from "solid-js";
938
1185
  var Atmosphere = (props) => {
939
1186
  const [ctx] = useMapContext();
940
- createEffect4(() => {
1187
+ createEffect5(() => {
941
1188
  if (ctx.isMapLibre) ctx.map.setSky?.(props.style || {});
942
1189
  else ctx.map.setFog(props.style || {});
943
1190
  });
@@ -953,7 +1200,7 @@ var Atmosphere = (props) => {
953
1200
 
954
1201
  // src/components/Terrain/index.tsx
955
1202
  import {
956
- createEffect as createEffect5,
1203
+ createEffect as createEffect6,
957
1204
  createUniqueId as createUniqueId4,
958
1205
  onCleanup as onCleanup6
959
1206
  } from "solid-js";
@@ -970,7 +1217,7 @@ var Terrain = (props) => {
970
1217
  });
971
1218
  ctx.map.sourceIdList.push(sourceId);
972
1219
  }
973
- createEffect5(() => {
1220
+ createEffect6(() => {
974
1221
  ctx.map.setTerrain({
975
1222
  exaggeration: props.exaggeration || 1,
976
1223
  source: props?.source || sourceId
@@ -981,13 +1228,13 @@ var Terrain = (props) => {
981
1228
  };
982
1229
 
983
1230
  // src/components/Control/index.tsx
984
- import { createSignal as createSignal3, createEffect as createEffect7, splitProps, untrack } from "solid-js";
1231
+ import { createSignal as createSignal3, createEffect as createEffect8, splitProps, untrack } from "solid-js";
985
1232
 
986
1233
  // src/lib/createMapControl.ts
987
- import { createEffect as createEffect6, onCleanup as onCleanup7 } from "solid-js";
1234
+ import { createEffect as createEffect7, onCleanup as onCleanup7 } from "solid-js";
988
1235
  var useControlPosition = (control, position) => {
989
1236
  const [ctx] = useMapContext();
990
- createEffect6(() => {
1237
+ createEffect7(() => {
991
1238
  const c = control();
992
1239
  const pos = position?.();
993
1240
  if (!c) return;
@@ -1014,7 +1261,7 @@ var Control = (props) => {
1014
1261
  ["logo", ctx.mapLib.LogoControl],
1015
1262
  ["terrain", ctx.mapLib.TerrainControl]
1016
1263
  ]);
1017
- createEffect7(() => {
1264
+ createEffect8(() => {
1018
1265
  untrack(
1019
1266
  () => control() && ctx.map.hasControl(control()) && ctx.map.removeControl(control())
1020
1267
  );
@@ -1027,7 +1274,7 @@ var Control = (props) => {
1027
1274
  };
1028
1275
 
1029
1276
  // src/components/Marker/index.tsx
1030
- import { onCleanup as onCleanup8, createEffect as createEffect8, splitProps as splitProps2 } from "solid-js";
1277
+ import { onCleanup as onCleanup8, createEffect as createEffect9, splitProps as splitProps2 } from "solid-js";
1031
1278
  var Marker = (props) => {
1032
1279
  if (!props.lngLat) throw new Error("Marker - lngLat is required");
1033
1280
  const [update, create_popup, create_marker] = splitProps2(
@@ -1039,7 +1286,7 @@ var Marker = (props) => {
1039
1286
  let marker = null;
1040
1287
  let popup = null;
1041
1288
  const LNGLAT = update.lngLat;
1042
- createEffect8(() => {
1289
+ createEffect9(() => {
1043
1290
  if (!ctx.map) return;
1044
1291
  popup?.remove();
1045
1292
  popup = new ctx.mapLib.Popup({
@@ -1047,31 +1294,31 @@ var Marker = (props) => {
1047
1294
  focusAfterOpen: false,
1048
1295
  ...create_popup.popup
1049
1296
  }).on("open", () => create_popup.onOpen?.()).on("close", () => create_popup.onClose?.());
1050
- createEffect8(() => {
1297
+ createEffect9(() => {
1051
1298
  if (update.children === void 0) return;
1052
1299
  typeof update.children === "string" ? popup?.setHTML(update.children) : popup?.setDOMContent(update.children);
1053
1300
  marker?.setPopup(popup);
1054
1301
  });
1055
- createEffect8(
1302
+ createEffect9(
1056
1303
  () => update.showPopup !== popup?.isOpen() && marker?.togglePopup()
1057
1304
  );
1058
1305
  });
1059
- createEffect8(() => {
1306
+ createEffect9(() => {
1060
1307
  if (!ctx.map) return;
1061
1308
  marker?.remove();
1062
1309
  marker = new ctx.mapLib.Marker(create_marker.options).on("dragstart", () => create_marker.onDragStart?.()).on("dragend", () => create_marker.onDragEnd?.()).on("drag", () => create_marker.onDrag?.(marker?.getLngLat().toArray())).setPopup(popup).setLngLat(LNGLAT).addTo(ctx.map);
1063
- createEffect8(
1310
+ createEffect9(
1064
1311
  () => update.showPopup !== popup?.isOpen() && marker?.togglePopup()
1065
1312
  );
1066
1313
  });
1067
- createEffect8(() => marker?.setLngLat(update.lngLat));
1068
- createEffect8(() => marker?.setDraggable(update.draggable));
1314
+ createEffect9(() => marker?.setLngLat(update.lngLat));
1315
+ createEffect9(() => marker?.setDraggable(update.draggable));
1069
1316
  onCleanup8(() => marker?.remove());
1070
1317
  return null;
1071
1318
  };
1072
1319
 
1073
1320
  // src/components/Popup/index.tsx
1074
- import { onCleanup as onCleanup9, createEffect as createEffect9, splitProps as splitProps3 } from "solid-js";
1321
+ import { onCleanup as onCleanup9, createEffect as createEffect10, splitProps as splitProps3 } from "solid-js";
1075
1322
  var Popup = (props) => {
1076
1323
  if (!props.trackPointer && !props.lngLat)
1077
1324
  throw new Error("Popup - lngLat or trackPointer is required");
@@ -1082,7 +1329,7 @@ var Popup = (props) => {
1082
1329
  ]);
1083
1330
  const [ctx] = useMapContext();
1084
1331
  let popup;
1085
- createEffect9(() => {
1332
+ createEffect10(() => {
1086
1333
  if (!ctx.map) return;
1087
1334
  popup?.remove();
1088
1335
  popup = new ctx.mapLib.Popup({
@@ -1090,10 +1337,10 @@ var Popup = (props) => {
1090
1337
  focusAfterOpen: false,
1091
1338
  ...create.options
1092
1339
  }).on("open", () => create.onOpen?.()).on("close", () => create.onClose?.()).addTo(ctx.map);
1093
- createEffect9(
1340
+ createEffect10(
1094
1341
  () => update.trackPointer ? popup.trackPointer() : popup.setLngLat(update.lngLat)
1095
1342
  );
1096
- createEffect9(
1343
+ createEffect10(
1097
1344
  () => typeof update.children === "string" ? popup.setHTML(update.children) : popup.setDOMContent(update.children)
1098
1345
  );
1099
1346
  });
@@ -1105,63 +1352,189 @@ var Popup = (props) => {
1105
1352
  import {
1106
1353
  onCleanup as onCleanup10,
1107
1354
  createSignal as createSignal4,
1108
- createEffect as createEffect10,
1355
+ createEffect as createEffect11,
1109
1356
  untrack as untrack2
1110
1357
  } from "solid-js";
1358
+
1359
+ // src/components/Image/sdf.ts
1360
+ var INF = 1e20;
1361
+ var ALPHA_TABLE = new Float64Array(256);
1362
+ for (let i = 0; i < 256; i++) {
1363
+ const d = 0.5 - Math.pow(i / 255, 1 / 2.2);
1364
+ ALPHA_TABLE[i] = d * Math.abs(d);
1365
+ }
1366
+ ALPHA_TABLE[255] = -INF;
1367
+ function edt1d(grid, offset, stride, length3, f, v, z) {
1368
+ v[0] = 0;
1369
+ z[0] = -INF;
1370
+ z[1] = INF;
1371
+ f[0] = grid[offset];
1372
+ for (let q = 1, k = 0, s = 0; q < length3; q++) {
1373
+ f[q] = grid[offset + q * stride];
1374
+ const q2 = q * q;
1375
+ do {
1376
+ const r = v[k];
1377
+ s = (f[q] - f[r] + q2 - r * r) / (q - r) / 2;
1378
+ } while (s <= z[k] && --k > -1);
1379
+ k++;
1380
+ v[k] = q;
1381
+ z[k] = s;
1382
+ z[k + 1] = INF;
1383
+ }
1384
+ for (let q = 0, k = 0; q < length3; q++) {
1385
+ while (z[k + 1] < q) k++;
1386
+ const r = v[k];
1387
+ const qr = q - r;
1388
+ grid[offset + q * stride] = f[r] + qr * qr;
1389
+ }
1390
+ }
1391
+ function edt(data, width, height, f, v, z) {
1392
+ for (let x = 0; x < width; x++) edt1d(data, x, width, height, f, v, z);
1393
+ for (let y = 0; y < height; y++) edt1d(data, y * width, 1, width, f, v, z);
1394
+ }
1395
+ var sdfPadding = ({ radius = 8 } = {}) => Math.ceil(radius);
1396
+ function toSDF(imageData, { radius = 8, cutoff = 0.25 } = {}) {
1397
+ const { width, height } = imageData;
1398
+ const len = width * height;
1399
+ const gridOuter = new Float64Array(len).fill(INF);
1400
+ const gridInner = new Float64Array(len);
1401
+ const size = Math.max(width, height);
1402
+ const f = new Float64Array(size);
1403
+ const v = new Uint16Array(size);
1404
+ const z = new Float64Array(size + 1);
1405
+ const src = imageData.data;
1406
+ for (let i = 0, j = 3; i < len; i++, j += 4) {
1407
+ const a = src[j];
1408
+ if (a === 0) continue;
1409
+ const t = ALPHA_TABLE[a];
1410
+ gridOuter[i] = Math.max(0, t);
1411
+ gridInner[i] = Math.max(0, -t);
1412
+ }
1413
+ edt(gridOuter, width, height, f, v, z);
1414
+ edt(gridInner, width, height, f, v, z);
1415
+ const scale = 255 / radius;
1416
+ const base = 255 * (1 - cutoff);
1417
+ const out = new Uint8ClampedArray(len * 4);
1418
+ for (let i = 0, j = 0; i < len; i++, j += 4) {
1419
+ const d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
1420
+ const value = Math.max(0, Math.min(255, Math.round(base - scale * d)));
1421
+ out[j] = out[j + 1] = out[j + 2] = 255;
1422
+ out[j + 3] = value;
1423
+ }
1424
+ return { width, height, data: out };
1425
+ }
1426
+
1427
+ // src/components/Image/shapes.ts
1111
1428
  var PATTERN = {
1112
1429
  diagonal_l: { size: 20, path: "M20 0 0 20M-10 10 10-10M10 30 30 10" },
1113
1430
  diagonal_r: { size: 20, path: "M0 0 20 20M30 10 10-10M10 30-10 10" },
1114
1431
  horizontal: { size: 14, path: "M7 0V20" },
1115
1432
  vertical: { size: 14, path: "M0 7H20" },
1433
+ // A single centered "+" per tile, tiling into a repeating grid of crosses — not to be
1434
+ // confused with `SYMBOL.cross` (a single bold "+" icon) below.
1116
1435
  cross: { size: 18, path: "M9 0V18M0 9H18" },
1117
1436
  hash: { size: 30, path: "M15 0 30 15 15 30 0 15Z" },
1118
1437
  chevron_h: { size: 20, path: "M-5 5 0 10 10 0 20 10 25 5M0 30 10 20 20 30" },
1119
1438
  chevron_v: { size: 20, path: "M5-5 10 0 0 10 10 20 5 25M25 15 20 10 25 5" },
1120
1439
  square: { size: 20, path: "M8 8H12V12H8Z", fill: true },
1121
1440
  hex: { size: 50, path: "M0 0V50L50 25ZM50 0V50L0 25ZM25 0V50" },
1441
+ // Two arcs swept almost all the way around (`A2.5 2.5 0 1 1 ...` to a point a hair past the
1442
+ // start) rather than a `<circle>`, since this path is later stroked/filled via `Path2D`,
1443
+ // which has no dedicated circle primitive. A single dot per tile — not to be confused with
1444
+ // `SYMBOL.circle` (a single full-bleed circle icon) below.
1122
1445
  circle: {
1123
1446
  size: 25,
1124
1447
  path: "M8 10A2.5 2.5 0 118.01 10M18 15A2.5 2.5 0 1018.01 15",
1125
1448
  fill: true
1126
- }
1449
+ },
1450
+ // `horizontal` and `vertical` combined into one crossed-line tile.
1451
+ grid: { size: 20, path: "M10 0V20M0 10H20" },
1452
+ // Two rows of offset (running-bond) bricks per tile, so the offset continues correctly
1453
+ // when tiles stack — row two's joints fall at the quarter-points, not centered, to match
1454
+ // a real brick course rather than just checkerboarding.
1455
+ brick: {
1456
+ size: 40,
1457
+ path: "M0 0H40M0 20H40M0 40H40M0 0V20M20 0V20M40 0V20M10 20V40M30 20V40"
1458
+ },
1459
+ // Two wavy horizontal lines per tile (quadratic-bezier "hump" repeated via the `T` shorthand),
1460
+ // for the wave/water fill texture common in geologic and land-cover maps.
1461
+ wave: { size: 20, path: "M0 5Q5 0 10 5T20 5M0 15Q5 10 10 15T20 15" }
1127
1462
  };
1128
1463
  var patternList = Object.keys(PATTERN);
1464
+ var svg = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">${body}</svg>`;
1465
+ var SYMBOL = {
1466
+ square: svg('<rect x="8" y="8" width="48" height="48" fill="#000"/>'),
1467
+ circle: svg('<circle cx="32" cy="32" r="28" fill="#000"/>'),
1468
+ triangle: svg('<polygon points="32,6 60,56 4,56" fill="#000"/>'),
1469
+ diamond: svg('<polygon points="32,4 60,32 32,60 4,32" fill="#000"/>'),
1470
+ pentagon: svg('<polygon points="32,4 59,23 48,55 16,55 5,23" fill="#000"/>'),
1471
+ hexagon: svg('<polygon points="32,4 56,18 56,46 32,60 8,46 8,18" fill="#000"/>'),
1472
+ // A square with corners cut at 2/7 of its side, the standard construction for a regular
1473
+ // octagon (stop-sign proportions) without resorting to trig.
1474
+ octagon: svg(
1475
+ '<polygon points="20,4 44,4 60,20 60,44 44,60 20,60 4,44 4,20" fill="#000"/>'
1476
+ ),
1477
+ cross: svg(
1478
+ '<polygon points="20,4 44,4 44,20 60,20 60,44 44,44 44,60 20,60 20,44 4,44 4,20 20,20" fill="#000"/>'
1479
+ ),
1480
+ // The same 12-point "+" as `cross` above, rotated 45° about the center.
1481
+ x: svg(
1482
+ '<polygon points="43,4 60,21 49,32 60,43 43,60 32,49 21,60 4,43 15,32 4,21 21,4 32,15" fill="#000"/>'
1483
+ ),
1484
+ // A regular 5-pointed star: outer vertices every 72° (radius 30) alternating with inner
1485
+ // vertices every 72° offset by 36° (radius ~11.5, the usual ratio for a balanced star).
1486
+ star: svg(
1487
+ '<polygon points="32,2 39,23 61,23 43,36 50,56 32,44 14,56 21,36 4,23 25,23" fill="#000"/>'
1488
+ )
1489
+ };
1490
+ var symbolList = Object.keys(SYMBOL);
1491
+ var wrapSymbolPath = (d) => svg(`<path d="${d}" fill="#000"/>`);
1492
+
1493
+ // src/components/Image/index.tsx
1129
1494
  var MGL_Image = (props) => {
1130
1495
  const [ctx] = useMapContext();
1131
1496
  const [size, setSize] = createSignal4({ width: 0, height: 0 });
1497
+ let latestImage = null;
1132
1498
  const debug = (text, value) => {
1133
1499
  ctx.map.debug && console.debug("%c[MapGL]", "color: #10b981", text, value || "");
1134
1500
  };
1501
+ const handleStyleLoad = () => {
1502
+ if (!latestImage || !ctx.map || ctx.map.hasImage(props.id)) return;
1503
+ ctx.map.addImage(props.id, latestImage.data, latestImage.ops);
1504
+ debug("Re-Add Image:", props.id);
1505
+ };
1506
+ ctx.map.on("style.load", handleStyleLoad);
1135
1507
  onCleanup10(() => {
1508
+ ctx.map?.off("style.load", handleStyleLoad);
1136
1509
  ctx.map?.hasImage(props.id) && ctx.map?.removeImage(props.id);
1137
1510
  debug("Remove Image:", props.id);
1138
1511
  });
1139
- createEffect10(() => {
1512
+ createEffect11(() => {
1140
1513
  if (!props.id) throw new Error("Image - ID is required");
1141
- if (!props.source && !props.pattern)
1142
- throw new Error("Image - Image or Pattern is required");
1143
- const ops = props.pattern ? { pixelRatio: 2, ...props.options } : props.options;
1144
- _loadImage(props.source || _createPattern(props.pattern), (data) => {
1514
+ if (!props.source && !props.pattern && !props.symbol)
1515
+ throw new Error("Image - Image, Pattern or Symbol is required");
1516
+ const pixelRatio = Math.max(1, Math.round(window.devicePixelRatio || 2));
1517
+ const ops = props.pattern ? { pixelRatio, ...props.options } : props.sdf ? { ...props.options, sdf: true } : props.options;
1518
+ const symbolSource = props.symbol && (SYMBOL[props.symbol] || (props.symbol.trimStart().startsWith("<svg") ? props.symbol : wrapSymbolPath(props.symbol)));
1519
+ _loadImage(props.source || symbolSource || _createPattern(props.pattern, pixelRatio), (data, autoPixelRatio) => {
1520
+ const finalOps = autoPixelRatio ? { ...ops, pixelRatio: autoPixelRatio } : ops;
1145
1521
  const { width, height } = data;
1146
1522
  if (ctx.map && !ctx.map.hasImage(props.id))
1147
- ctx.map.addImage(props.id, data, ops);
1523
+ ctx.map.addImage(props.id, data, finalOps);
1148
1524
  if (!props.pattern && untrack2(() => width === size().width && height === size().height)) {
1149
1525
  ctx.map.updateImage(props.id, data);
1150
1526
  ctx.map.triggerRepaint();
1151
1527
  } else {
1152
1528
  ctx.map.removeImage(props.id);
1153
- ctx.map.addImage(props.id, data, ops);
1529
+ ctx.map.addImage(props.id, data, finalOps);
1154
1530
  }
1155
1531
  setSize({ width, height });
1532
+ latestImage = { data, ops: finalOps };
1156
1533
  debug("Add Image:", props.id);
1157
- ctx.map.on("style.load", () => {
1158
- if (ctx.map.hasImage(props.id)) return;
1159
- ctx.map.addImage(props.id, data, ops);
1160
- debug("Re-Add Image:", props.id);
1161
- });
1162
1534
  });
1163
1535
  });
1164
1536
  const _loadImage = (image, callback) => {
1537
+ const sdfOpts = props.sdf ? typeof props.sdf === "object" ? props.sdf : {} : null;
1165
1538
  if (typeof image == "string" && image?.trimStart().startsWith("<svg")) {
1166
1539
  image = new DOMParser().parseFromString(image, "image/svg+xml").childNodes[0];
1167
1540
  }
@@ -1184,36 +1557,63 @@ var MGL_Image = (props) => {
1184
1557
  const img = new Image();
1185
1558
  img.crossOrigin = "Anonymous";
1186
1559
  img.onload = () => {
1560
+ const floorScale = Math.max(1, 50 / Math.min(img.width, img.height));
1561
+ const pixelRatio = props.options?.pixelRatio ?? Math.max(1, Math.round(window.devicePixelRatio || 1));
1562
+ const scale = floorScale * pixelRatio;
1563
+ const padding = sdfOpts ? sdfPadding(sdfOpts) * pixelRatio : 0;
1564
+ const drawWidth = Math.round(img.width * scale);
1565
+ const drawHeight = Math.round(img.height * scale);
1187
1566
  const canvas = document.createElement("canvas");
1188
- canvas.width = Math.max(img.width, 50);
1189
- canvas.height = Math.max(img.height, 50);
1567
+ canvas.width = drawWidth + padding * 2;
1568
+ canvas.height = drawHeight + padding * 2;
1190
1569
  const ctx2 = canvas.getContext("2d");
1191
1570
  ctx2.imageSmoothingEnabled = true;
1192
- ctx2.drawImage(img, 0, 0, canvas.width, canvas.height);
1193
- return callback(ctx2.getImageData(0, 0, canvas.width, canvas.height));
1571
+ ctx2.drawImage(img, padding, padding, drawWidth, drawHeight);
1572
+ const imageData2 = ctx2.getImageData(0, 0, canvas.width, canvas.height);
1573
+ return callback(
1574
+ sdfOpts ? toSDF(imageData2, sdfOpts) : imageData2,
1575
+ pixelRatio
1576
+ );
1194
1577
  };
1195
- img.src = image.trimStart().startsWith("<svg") ? "data:image/svg+xml;charset=utf-8," + image : image;
1578
+ img.onerror = (event) => console.error(`[MapGL] Image "${props.id}" failed to load:`, event);
1579
+ img.src = image.trimStart().startsWith("<svg") ? "data:image/svg+xml;charset=utf-8," + encodeURIComponent(image) : image;
1196
1580
  } else {
1197
- return callback(imageData);
1581
+ if (!sdfOpts) return callback(imageData);
1582
+ const { width, height } = imageData;
1583
+ const padding = sdfPadding(sdfOpts);
1584
+ const canvas = document.createElement("canvas");
1585
+ canvas.width = width + padding * 2;
1586
+ canvas.height = height + padding * 2;
1587
+ const c = canvas.getContext("2d");
1588
+ c.imageSmoothingEnabled = true;
1589
+ if ("data" in imageData) {
1590
+ c.putImageData(imageData, padding, padding);
1591
+ } else {
1592
+ c.drawImage(imageData, padding, padding, width, height);
1593
+ }
1594
+ const pixels = c.getImageData(0, 0, canvas.width, canvas.height);
1595
+ return callback(toSDF(pixels, sdfOpts));
1198
1596
  }
1199
1597
  });
1200
1598
  };
1201
- const _createPattern = (pattern) => {
1599
+ const _createPattern = (pattern, pixelRatio = 2) => {
1202
1600
  const p = PATTERN[pattern.type];
1601
+ const size2 = p.size * pixelRatio;
1203
1602
  return {
1204
- width: p.size,
1205
- height: p.size,
1206
- data: new Uint8Array(p.size * p.size * 4),
1603
+ width: size2,
1604
+ height: size2,
1605
+ data: new Uint8Array(size2 * size2 * 4),
1207
1606
  onAdd: function() {
1208
- let canvas = document.createElement("canvas");
1209
- canvas.width = canvas.height = p.size;
1607
+ const canvas = document.createElement("canvas");
1608
+ canvas.width = canvas.height = size2;
1210
1609
  this.ctx = canvas.getContext("2d", { willReadFrequently: true });
1610
+ this.ctx.scale(pixelRatio, pixelRatio);
1211
1611
  },
1212
1612
  render: function() {
1213
1613
  this.ctx.fillStyle = pattern.background || "transparent";
1214
- this.ctx.fillRect(0, 0, this.width, this.height);
1614
+ this.ctx.fillRect(0, 0, p.size, p.size);
1215
1615
  this.ctx.strokeStyle = this.ctx.fillStyle = pattern.color || "black";
1216
- this.ctx.lineWidth = pattern.lineWith || 1;
1616
+ this.ctx.lineWidth = pattern.lineWidth || 1;
1217
1617
  this.ctx.stroke(new Path2D(p.path));
1218
1618
  if (p.fill) this.ctx.fill(new Path2D(p.path));
1219
1619
  this.data = this.ctx.getImageData(0, 0, this.width, this.height).data;
@@ -1225,7 +1625,7 @@ var MGL_Image = (props) => {
1225
1625
  };
1226
1626
 
1227
1627
  // src/components/Camera/index.tsx
1228
- import { createEffect as createEffect11, onCleanup as onCleanup11, createSignal as createSignal5 } from "solid-js";
1628
+ import { createEffect as createEffect12, onCleanup as onCleanup11, createSignal as createSignal5 } from "solid-js";
1229
1629
  var lerp = (a, b, t) => a.map((_, idx) => (1 - t) * a[idx] + t * b[idx]);
1230
1630
  var slerp = (a, b, t) => {
1231
1631
  const dotProduct = a.map((_, idx) => a[idx] * b[idx]).reduce((m, n) => m + n);
@@ -1272,7 +1672,7 @@ var Camera = (props) => {
1272
1672
  isReverse = !isReverse;
1273
1673
  animationTime = isReverse ? animationTime - 1e-3 : animationTime + 1e-3;
1274
1674
  };
1275
- createEffect11(() => {
1675
+ createEffect12(() => {
1276
1676
  props.translate && window.requestAnimationFrame(frame);
1277
1677
  });
1278
1678
  const options = { duration: 1e3, easing: (n) => n };
@@ -1307,7 +1707,7 @@ var Camera = (props) => {
1307
1707
  const onEnd = () => props.rotateGlobe ? rotateGlobe() : rotateViewport();
1308
1708
  ctx.map.on("moveend", onEnd).on("dragend", onEnd);
1309
1709
  let originalCenter;
1310
- createEffect11(() => {
1710
+ createEffect12(() => {
1311
1711
  if (props.rotateGlobe == void 0) return;
1312
1712
  if (props.rotateGlobe) {
1313
1713
  originalCenter = ctx.map.getCenter();
@@ -1316,7 +1716,7 @@ var Camera = (props) => {
1316
1716
  props.resetWhenStopped ? ctx.map.stop().easeTo({ center: originalCenter }) : ctx.map.stop();
1317
1717
  }
1318
1718
  });
1319
- createEffect11(() => {
1719
+ createEffect12(() => {
1320
1720
  if (props.rotateViewport == void 0) return;
1321
1721
  props.rotateViewport ? rotateViewport() : props.resetWhenStopped ? ctx.map.stop().resetNorthPitch() : ctx.map.stop();
1322
1722
  });
@@ -1325,10 +1725,10 @@ var Camera = (props) => {
1325
1725
  };
1326
1726
 
1327
1727
  // src/components/Light/index.tsx
1328
- import { onCleanup as onCleanup12, createEffect as createEffect12 } from "solid-js";
1728
+ import { onCleanup as onCleanup12, createEffect as createEffect13 } from "solid-js";
1329
1729
  var Light = (props) => {
1330
1730
  const [ctx] = useMapContext();
1331
- createEffect12(() => ctx.map.setLight(props.style || {}));
1731
+ createEffect13(() => ctx.map.setLight(props.style || {}));
1332
1732
  onCleanup12(() => ctx.map?.setLight(null));
1333
1733
  return null;
1334
1734
  };
@@ -2285,7 +2685,7 @@ var Draw = (props) => {
2285
2685
  };
2286
2686
 
2287
2687
  // src/components/DeckOverlay/index.tsx
2288
- import { createEffect as createEffect13 } from "solid-js";
2688
+ import { createEffect as createEffect14 } from "solid-js";
2289
2689
  var DeckOverlay = (props) => {
2290
2690
  const [ctx] = useMapContext();
2291
2691
  let overlay;
@@ -2294,7 +2694,7 @@ var DeckOverlay = (props) => {
2294
2694
  return overlay;
2295
2695
  };
2296
2696
  useControlPosition(control);
2297
- createEffect13(() => overlay?.setProps(props.props || {}));
2697
+ createEffect14(() => overlay?.setProps(props.props || {}));
2298
2698
  return null;
2299
2699
  };
2300
2700
  export {
@@ -2313,8 +2713,13 @@ export {
2313
2713
  Source,
2314
2714
  Terrain,
2315
2715
  MapGL as default,
2716
+ disableRasterFade,
2316
2717
  patternList,
2718
+ settleAfterIdle,
2719
+ symbolList,
2720
+ toSDF,
2317
2721
  useMapContext,
2318
2722
  useScene,
2319
- useSourceId
2723
+ useSourceId,
2724
+ waitForIdleAndSettle
2320
2725
  };