@page-speed/maps 0.1.4 → 0.1.6

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 (46) hide show
  1. package/README.md +120 -0
  2. package/dist/components/geo-map.cjs +1237 -0
  3. package/dist/components/geo-map.cjs.map +1 -0
  4. package/dist/components/geo-map.d.cts +138 -0
  5. package/dist/components/geo-map.d.ts +138 -0
  6. package/dist/components/geo-map.js +1216 -0
  7. package/dist/components/geo-map.js.map +1 -0
  8. package/dist/components/index.cjs +1359 -0
  9. package/dist/components/index.cjs.map +1 -0
  10. package/dist/components/index.d.cts +5 -0
  11. package/dist/components/index.d.ts +5 -0
  12. package/dist/components/index.js +1335 -0
  13. package/dist/components/index.js.map +1 -0
  14. package/dist/components/map-marker.cjs +137 -0
  15. package/dist/components/map-marker.cjs.map +1 -0
  16. package/dist/components/map-marker.d.cts +76 -0
  17. package/dist/components/map-marker.d.ts +76 -0
  18. package/dist/components/map-marker.js +130 -0
  19. package/dist/components/map-marker.js.map +1 -0
  20. package/dist/index.cjs +929 -21
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +2 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +910 -21
  25. package/dist/index.js.map +1 -1
  26. package/dist/types/index.d.cts +5 -5
  27. package/dist/types/index.d.ts +5 -5
  28. package/dist/utils/cn.cjs +13 -0
  29. package/dist/utils/cn.cjs.map +1 -0
  30. package/dist/utils/cn.d.cts +16 -0
  31. package/dist/utils/cn.d.ts +16 -0
  32. package/dist/utils/cn.js +11 -0
  33. package/dist/utils/cn.js.map +1 -0
  34. package/dist/utils/index.cjs +63 -0
  35. package/dist/utils/index.cjs.map +1 -1
  36. package/dist/utils/index.d.cts +4 -0
  37. package/dist/utils/index.d.ts +4 -0
  38. package/dist/utils/index.js +42 -1
  39. package/dist/utils/index.js.map +1 -1
  40. package/dist/utils/simple-pressable.cjs +63 -0
  41. package/dist/utils/simple-pressable.cjs.map +1 -0
  42. package/dist/utils/simple-pressable.d.cts +20 -0
  43. package/dist/utils/simple-pressable.d.ts +20 -0
  44. package/dist/utils/simple-pressable.js +41 -0
  45. package/dist/utils/simple-pressable.js.map +1 -0
  46. package/package.json +29 -2
package/dist/index.cjs CHANGED
@@ -1,12 +1,30 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
3
+ var React3 = require('react');
4
4
  var maplibre = require('react-map-gl/maplibre');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
5
7
  var jsxRuntime = require('react/jsx-runtime');
6
8
 
7
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
8
26
 
9
- var React__default = /*#__PURE__*/_interopDefault(React);
27
+ var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
10
28
 
11
29
  // src/core/MapLibre.tsx
12
30
 
@@ -85,6 +103,42 @@ function generateGoogleMapLink(latitude, longitude, zoom = 15) {
85
103
  function generateGoogleDirectionsLink(latitude, longitude) {
86
104
  return `https://www.google.com/maps/dir/?api=1&destination=${latitude},${longitude}`;
87
105
  }
106
+ function cn(...inputs) {
107
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
108
+ }
109
+ var SimplePressable = React3__namespace.forwardRef(({ children, className, href, onClick, ...props }, ref) => {
110
+ if (href) {
111
+ const isExternal = href.startsWith("http://") || href.startsWith("https://");
112
+ return /* @__PURE__ */ jsxRuntime.jsx(
113
+ "a",
114
+ {
115
+ ref,
116
+ href,
117
+ className,
118
+ target: isExternal ? "_blank" : props.target,
119
+ rel: isExternal ? "noopener noreferrer" : props.rel,
120
+ onClick,
121
+ ...props,
122
+ children
123
+ }
124
+ );
125
+ }
126
+ if (onClick) {
127
+ return /* @__PURE__ */ jsxRuntime.jsx(
128
+ "button",
129
+ {
130
+ ref,
131
+ type: "button",
132
+ className,
133
+ onClick,
134
+ ...props,
135
+ children
136
+ }
137
+ );
138
+ }
139
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children });
140
+ });
141
+ SimplePressable.displayName = "SimplePressable";
88
142
  var DEFAULT_MAPLIBRE_CSS_HREF = "https://cdn.jsdelivr.net/npm/maplibre-gl@5.18.0/dist/maplibre-gl.css";
89
143
  var MAPLIBRE_STYLESHEET_ID = "page-speed-maplibre-gl-css";
90
144
  var DEFAULT_FLY_TO_OPTIONS = Object.freeze({});
@@ -211,18 +265,18 @@ function MapLibre({
211
265
  geolocateControlPosition = "top-left",
212
266
  flyToOptions = DEFAULT_FLY_TO_OPTIONS
213
267
  }) {
214
- const mapRef = React__default.default.useRef(null);
268
+ const mapRef = React3__namespace.default.useRef(null);
215
269
  const resolvedMapLibreCssHref = mapLibreCssHref && mapLibreCssHref.trim().length > 0 ? mapLibreCssHref : DEFAULT_MAPLIBRE_CSS_HREF;
216
- const [internalViewState, setInternalViewState] = React__default.default.useState({
270
+ const [internalViewState, setInternalViewState] = React3__namespace.default.useState({
217
271
  latitude: viewState?.latitude ?? center.lat,
218
272
  longitude: viewState?.longitude ?? center.lng,
219
273
  zoom: viewState?.zoom ?? zoom
220
274
  });
221
- const isUserInteracting = React__default.default.useRef(false);
222
- const isMarkerDragging = React__default.default.useRef(false);
223
- const dragAnimationFrame = React__default.default.useRef(null);
224
- const lastReportedViewState = React__default.default.useRef(null);
225
- const resolvedFlyToOptions = React__default.default.useMemo(
275
+ const isUserInteracting = React3__namespace.default.useRef(false);
276
+ const isMarkerDragging = React3__namespace.default.useRef(false);
277
+ const dragAnimationFrame = React3__namespace.default.useRef(null);
278
+ const lastReportedViewState = React3__namespace.default.useRef(null);
279
+ const resolvedFlyToOptions = React3__namespace.default.useMemo(
226
280
  () => ({
227
281
  speed: flyToOptions.speed ?? 0.8,
228
282
  curve: flyToOptions.curve ?? 1.2,
@@ -236,10 +290,10 @@ function MapLibre({
236
290
  flyToOptions.speed
237
291
  ]
238
292
  );
239
- React__default.default.useEffect(() => {
293
+ React3__namespace.default.useEffect(() => {
240
294
  ensureMapLibreStylesheet(resolvedMapLibreCssHref);
241
295
  }, [resolvedMapLibreCssHref]);
242
- React__default.default.useEffect(() => {
296
+ React3__namespace.default.useEffect(() => {
243
297
  if (!mapRef.current || !viewState || isUserInteracting.current || isMarkerDragging.current) {
244
298
  return;
245
299
  }
@@ -273,10 +327,10 @@ function MapLibre({
273
327
  viewState?.longitude,
274
328
  viewState?.zoom
275
329
  ]);
276
- const handleMoveStart = React__default.default.useCallback(() => {
330
+ const handleMoveStart = React3__namespace.default.useCallback(() => {
277
331
  isUserInteracting.current = true;
278
332
  }, []);
279
- const handleMove = React__default.default.useCallback(
333
+ const handleMove = React3__namespace.default.useCallback(
280
334
  (event) => {
281
335
  const nextViewState = event.viewState;
282
336
  setInternalViewState({
@@ -294,7 +348,7 @@ function MapLibre({
294
348
  },
295
349
  [onViewStateChange]
296
350
  );
297
- const handleMoveEnd = React__default.default.useCallback(
351
+ const handleMoveEnd = React3__namespace.default.useCallback(
298
352
  (event) => {
299
353
  isUserInteracting.current = false;
300
354
  if (!onMoveEnd) {
@@ -315,7 +369,7 @@ function MapLibre({
315
369
  },
316
370
  [onMoveEnd]
317
371
  );
318
- const handleMapClick = React__default.default.useCallback(
372
+ const handleMapClick = React3__namespace.default.useCallback(
319
373
  (event) => {
320
374
  if (!onClick) {
321
375
  return;
@@ -324,11 +378,11 @@ function MapLibre({
324
378
  },
325
379
  [onClick]
326
380
  );
327
- const normalizedMarkers = React__default.default.useMemo(
381
+ const normalizedMarkers = React3__namespace.default.useMemo(
328
382
  () => normalizeMarkers(markers),
329
383
  [markers]
330
384
  );
331
- const markerElements = React__default.default.useMemo(
385
+ const markerElements = React3__namespace.default.useMemo(
332
386
  () => normalizedMarkers.map((marker) => /* @__PURE__ */ jsxRuntime.jsx(
333
387
  maplibre.Marker,
334
388
  {
@@ -415,7 +469,7 @@ function MapLibre({
415
469
  )),
416
470
  [normalizedMarkers, onMarkerDrag]
417
471
  );
418
- const resolvedMapStyleUrl = React__default.default.useMemo(() => {
472
+ const resolvedMapStyleUrl = React3__namespace.default.useMemo(() => {
419
473
  if (styleUrl) {
420
474
  return appendStadiaApiKey(styleUrl, stadiaApiKey);
421
475
  }
@@ -482,7 +536,7 @@ function computeGeoCenter(coordinates) {
482
536
  return { lat, lng };
483
537
  }
484
538
  function useGeoCenter(coordinates) {
485
- return React.useMemo(() => computeGeoCenter(coordinates), [coordinates]);
539
+ return React3.useMemo(() => computeGeoCenter(coordinates), [coordinates]);
486
540
  }
487
541
  var TILE_SIZE = 512;
488
542
  function latToMercatorY(lat) {
@@ -540,7 +594,7 @@ function computeDefaultZoom(options) {
540
594
  }
541
595
  function useDefaultZoom(options) {
542
596
  const { coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom } = options;
543
- return React.useMemo(
597
+ return React3.useMemo(
544
598
  () => computeDefaultZoom({
545
599
  coordinates,
546
600
  mapWidth,
@@ -552,12 +606,866 @@ function useDefaultZoom(options) {
552
606
  [coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom]
553
607
  );
554
608
  }
609
+ var SIZE_CONFIG = {
610
+ sm: {
611
+ outer: "size-10",
612
+ middle: "size-7",
613
+ inner: "size-5",
614
+ dot: "size-2"
615
+ },
616
+ md: {
617
+ outer: "size-14",
618
+ middle: "size-10",
619
+ inner: "size-7",
620
+ dot: "size-2.5"
621
+ },
622
+ lg: {
623
+ outer: "size-20",
624
+ middle: "size-14",
625
+ inner: "size-10",
626
+ dot: "size-3.5"
627
+ }
628
+ };
629
+ function MapMarker({
630
+ size = "md",
631
+ isSelected = false,
632
+ dotColor,
633
+ innerRingColor,
634
+ middleRingColor,
635
+ outerRingColor,
636
+ className,
637
+ onClick,
638
+ interactive = true,
639
+ "aria-label": ariaLabel = "Map location marker"
640
+ }) {
641
+ const sizeConfig = SIZE_CONFIG[size];
642
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(
643
+ "div",
644
+ {
645
+ className: cn(
646
+ "relative flex items-center justify-center rounded-full transition-transform duration-200",
647
+ sizeConfig.outer,
648
+ isSelected && "scale-110",
649
+ className
650
+ ),
651
+ style: { backgroundColor: outerRingColor },
652
+ children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx(
654
+ "div",
655
+ {
656
+ className: cn(
657
+ "absolute rounded-full transition-all duration-200",
658
+ sizeConfig.middle
659
+ ),
660
+ style: { backgroundColor: middleRingColor }
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsxRuntime.jsx(
664
+ "div",
665
+ {
666
+ className: cn(
667
+ "absolute rounded-full transition-all duration-200",
668
+ sizeConfig.inner
669
+ ),
670
+ style: { backgroundColor: innerRingColor }
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsxRuntime.jsx(
674
+ "div",
675
+ {
676
+ className: cn(
677
+ "absolute rounded-full transition-all duration-200",
678
+ sizeConfig.dot
679
+ ),
680
+ style: { backgroundColor: dotColor }
681
+ }
682
+ )
683
+ ]
684
+ }
685
+ );
686
+ if (!interactive) {
687
+ return content;
688
+ }
689
+ return /* @__PURE__ */ jsxRuntime.jsx(
690
+ "button",
691
+ {
692
+ type: "button",
693
+ className: "group cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-full",
694
+ onClick,
695
+ "aria-label": ariaLabel,
696
+ children: /* @__PURE__ */ jsxRuntime.jsx(
697
+ "div",
698
+ {
699
+ className: cn(
700
+ "transition-transform duration-200 group-hover:scale-110",
701
+ isSelected && "scale-110"
702
+ ),
703
+ children: content
704
+ }
705
+ )
706
+ }
707
+ );
708
+ }
709
+ function NeutralMapMarker(props) {
710
+ return /* @__PURE__ */ jsxRuntime.jsx(
711
+ MapMarker,
712
+ {
713
+ ...props,
714
+ dotColor: "hsl(var(--neutral-900, 0 0% 9%))",
715
+ innerRingColor: "hsl(var(--neutral-400, 0 0% 64%))",
716
+ middleRingColor: "hsl(var(--neutral-300, 0 0% 78%))",
717
+ outerRingColor: "hsl(var(--neutral-200, 0 0% 88%))"
718
+ }
719
+ );
720
+ }
721
+ function createMapMarkerElement(config) {
722
+ return function MarkerElement({ isSelected }) {
723
+ return /* @__PURE__ */ jsxRuntime.jsx(MapMarker, { ...config, isSelected, interactive: false });
724
+ };
725
+ }
726
+ var PANEL_POSITION_CLASS = {
727
+ "top-left": "left-4 top-4",
728
+ "top-right": "right-4 top-4",
729
+ "bottom-left": "bottom-4 left-4",
730
+ "bottom-right": "bottom-4 right-4"
731
+ };
732
+ var DEFAULT_VIEW_STATE = {
733
+ latitude: 39.5,
734
+ longitude: -98.35,
735
+ zoom: 3
736
+ };
737
+ var VIDEO_FILE_EXTENSION_REGEX = /\.(mp4|webm|ogg|mov|m4v|m3u8)(\?.*)?$/i;
738
+ function resolveMediaType(item) {
739
+ if (item.type) {
740
+ return item.type;
741
+ }
742
+ return VIDEO_FILE_EXTENSION_REGEX.test(item.src) ? "video" : "image";
743
+ }
744
+ function normalizeId(value, fallback) {
745
+ if (value === null || value === void 0 || value === "") {
746
+ return fallback;
747
+ }
748
+ return String(value);
749
+ }
750
+ function buildClusterCenter(markers) {
751
+ if (!markers.length) {
752
+ return null;
753
+ }
754
+ const total = markers.reduce(
755
+ (accumulator, marker) => ({
756
+ latitude: accumulator.latitude + marker.latitude,
757
+ longitude: accumulator.longitude + marker.longitude
758
+ }),
759
+ { latitude: 0, longitude: 0 }
760
+ );
761
+ return {
762
+ latitude: total.latitude / markers.length,
763
+ longitude: total.longitude / markers.length
764
+ };
765
+ }
766
+ function resolveActionKey(action, index) {
767
+ if (typeof action.label === "string" && action.label.trim().length > 0) {
768
+ return `label:${action.label}:${index}`;
769
+ }
770
+ if (action.href) {
771
+ return `href:${action.href}:${index}`;
772
+ }
773
+ return `action:${index}`;
774
+ }
775
+ var FallbackIcon = ({ size = 20, className }) => /* @__PURE__ */ jsxRuntime.jsx(
776
+ "svg",
777
+ {
778
+ width: size,
779
+ height: size,
780
+ viewBox: "0 0 24 24",
781
+ fill: "none",
782
+ stroke: "currentColor",
783
+ strokeWidth: "2",
784
+ strokeLinecap: "round",
785
+ strokeLinejoin: "round",
786
+ className,
787
+ children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" })
788
+ }
789
+ );
790
+ var FallbackImg = ({ src, alt, className, loading }) => /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className, loading });
791
+ function MarkerActions({ actions }) {
792
+ if (!actions || actions.length === 0) {
793
+ return null;
794
+ }
795
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex flex-wrap gap-2", children: actions.map((action, index) => {
796
+ const {
797
+ label,
798
+ icon,
799
+ iconAfter,
800
+ children,
801
+ href,
802
+ onClick,
803
+ className: actionClassName,
804
+ variant,
805
+ size,
806
+ asButton,
807
+ ...rest
808
+ } = action;
809
+ const buttonStyles = cn(
810
+ "inline-flex items-center gap-2 px-4 py-2 rounded-md font-medium transition-colors",
811
+ variant === "outline" ? "border border-border bg-background hover:bg-muted" : "bg-primary text-primary-foreground hover:bg-primary/90",
812
+ size === "sm" && "text-sm px-3 py-1.5",
813
+ size === "icon" && "p-2",
814
+ actionClassName
815
+ );
816
+ return /* @__PURE__ */ jsxRuntime.jsx(
817
+ SimplePressable,
818
+ {
819
+ href,
820
+ onClick,
821
+ className: buttonStyles,
822
+ ...rest,
823
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
824
+ icon,
825
+ label,
826
+ iconAfter
827
+ ] })
828
+ },
829
+ resolveActionKey(action, index)
830
+ );
831
+ }) });
832
+ }
833
+ function MarkerMediaCarousel({
834
+ mediaItems,
835
+ optixFlowConfig,
836
+ IconComponent = FallbackIcon,
837
+ ImgComponent = FallbackImg
838
+ }) {
839
+ const [activeIndex, setActiveIndex] = React3__namespace.useState(0);
840
+ const totalItems = mediaItems.length;
841
+ const mediaResetKey = React3__namespace.useMemo(
842
+ () => mediaItems.map((item, index) => {
843
+ const itemId = normalizeId(item.id, `media-${index}`);
844
+ return `${itemId}:${item.src}:${item.type ?? ""}:${item.poster ?? ""}`;
845
+ }).join("|"),
846
+ [mediaItems]
847
+ );
848
+ const activeItemIndex = Math.min(activeIndex, Math.max(0, totalItems - 1));
849
+ React3__namespace.useEffect(() => {
850
+ setActiveIndex(0);
851
+ }, [mediaResetKey]);
852
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative border-b border-border/60 bg-muted/40", children: [
853
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative aspect-video w-full overflow-hidden", children: mediaItems.map((item, index) => {
854
+ const isActive = index === activeItemIndex;
855
+ const mediaType = resolveMediaType(item);
856
+ return /* @__PURE__ */ jsxRuntime.jsx(
857
+ "div",
858
+ {
859
+ "aria-hidden": !isActive,
860
+ className: cn(
861
+ "absolute inset-0 transition-opacity duration-500 ease-in-out",
862
+ isActive ? "opacity-100 z-1" : "opacity-0 z-0 pointer-events-none"
863
+ ),
864
+ children: mediaType === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
865
+ "video",
866
+ {
867
+ className: "h-full w-full object-cover",
868
+ controls: isActive,
869
+ preload: "metadata",
870
+ poster: item.poster,
871
+ tabIndex: isActive ? 0 : -1,
872
+ children: /* @__PURE__ */ jsxRuntime.jsx("source", { src: item.src })
873
+ }
874
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
875
+ ImgComponent,
876
+ {
877
+ src: item.src,
878
+ alt: item.alt ?? "Map marker media",
879
+ className: "h-full w-full object-cover",
880
+ loading: "eager",
881
+ optixFlowConfig
882
+ }
883
+ )
884
+ },
885
+ normalizeId(item.id, `media-slide-${index}`)
886
+ );
887
+ }) }),
888
+ totalItems > 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
889
+ /* @__PURE__ */ jsxRuntime.jsx(
890
+ "button",
891
+ {
892
+ type: "button",
893
+ "aria-label": "Show previous media",
894
+ className: "absolute left-4 top-1/2 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-2xl bg-card text-card-foreground shadow-lg border-4 border-black hover:border-white hover:bg-black hover:text-white transition-all duration-500 z-2",
895
+ onClick: () => {
896
+ setActiveIndex(
897
+ (current) => (current - 1 + totalItems) % totalItems
898
+ );
899
+ },
900
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { name: "lucide/arrow-left", size: 18 })
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsxRuntime.jsx(
904
+ "button",
905
+ {
906
+ type: "button",
907
+ "aria-label": "Show next media",
908
+ className: "absolute right-4 top-1/2 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-2xl bg-card text-card-foreground shadow-lg border-4 border-black hover:border-white hover:bg-black hover:text-white transition-all duration-500 z-2",
909
+ onClick: () => {
910
+ setActiveIndex((current) => (current + 1) % totalItems);
911
+ },
912
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { name: "lucide/arrow-right", size: 18 })
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-2 left-1/2 flex -translate-x-1/2 items-center gap-1.5 z-[2]", children: mediaItems.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
916
+ "button",
917
+ {
918
+ type: "button",
919
+ "aria-label": `Show media item ${index + 1}`,
920
+ className: cn(
921
+ "h-2 rounded-full transition-all duration-300",
922
+ index === activeItemIndex ? "w-6 bg-card" : "w-2 bg-card opacity-50 hover:opacity-100"
923
+ ),
924
+ onClick: () => setActiveIndex(index)
925
+ },
926
+ normalizeId(item.id, `media-dot-${index}`)
927
+ )) })
928
+ ] }) : null
929
+ ] });
930
+ }
931
+ function getMarkerTitle(marker, markerIndex) {
932
+ if (marker.title !== void 0 && marker.title !== null) {
933
+ return marker.title;
934
+ }
935
+ if (marker.label !== void 0 && marker.label !== null) {
936
+ return marker.label;
937
+ }
938
+ return `Location ${markerIndex + 1}`;
939
+ }
940
+ function GeoMap({
941
+ className,
942
+ mapWrapperClassName,
943
+ mapClassName,
944
+ panelClassName,
945
+ panelPosition = "top-left",
946
+ stadiaApiKey = "",
947
+ mapStyle = "osm-bright",
948
+ styleUrl,
949
+ mapLibreCssHref,
950
+ markers = [],
951
+ clusters = [],
952
+ viewState,
953
+ defaultViewState,
954
+ onViewStateChange,
955
+ onMapClick,
956
+ onMarkerDrag,
957
+ showNavigationControl = true,
958
+ showGeolocateControl = false,
959
+ navigationControlPosition = "top-right",
960
+ geolocateControlPosition = "top-left",
961
+ flyToOptions,
962
+ markerFocusZoom = 14,
963
+ clusterFocusZoom = 5,
964
+ selectedMarkerId,
965
+ initialSelectedMarkerId,
966
+ onSelectionChange,
967
+ clearSelectionOnMapClick = true,
968
+ mapChildren,
969
+ optixFlowConfig,
970
+ IconComponent = FallbackIcon,
971
+ ImgComponent = FallbackImg
972
+ }) {
973
+ const normalizedStandaloneMarkers = React3__namespace.useMemo(
974
+ () => markers.map((marker, index) => ({
975
+ ...marker,
976
+ id: normalizeId(marker.id, `marker-${index}`)
977
+ })),
978
+ [markers]
979
+ );
980
+ const normalizedClusters = React3__namespace.useMemo(() => {
981
+ const results = [];
982
+ clusters.forEach((cluster, clusterIndex) => {
983
+ const clusterId = normalizeId(cluster.id, `cluster-${clusterIndex}`);
984
+ const normalizedClusterMarkers = cluster.markers.map(
985
+ (marker, markerIndex) => ({
986
+ ...marker,
987
+ id: normalizeId(marker.id, `${clusterId}-marker-${markerIndex}`),
988
+ clusterId
989
+ })
990
+ );
991
+ const clusterCenter = cluster.latitude !== void 0 && cluster.longitude !== void 0 ? { latitude: cluster.latitude, longitude: cluster.longitude } : buildClusterCenter(normalizedClusterMarkers);
992
+ if (!clusterCenter) {
993
+ return;
994
+ }
995
+ results.push({
996
+ ...cluster,
997
+ id: clusterId,
998
+ latitude: clusterCenter.latitude,
999
+ longitude: clusterCenter.longitude,
1000
+ markers: normalizedClusterMarkers
1001
+ });
1002
+ });
1003
+ return results;
1004
+ }, [clusters]);
1005
+ const markerLookup = React3__namespace.useMemo(() => {
1006
+ const lookup = /* @__PURE__ */ new Map();
1007
+ normalizedStandaloneMarkers.forEach((marker) => {
1008
+ lookup.set(marker.id, marker);
1009
+ });
1010
+ normalizedClusters.forEach((cluster) => {
1011
+ cluster.markers.forEach((marker) => {
1012
+ lookup.set(marker.id, marker);
1013
+ });
1014
+ });
1015
+ return lookup;
1016
+ }, [normalizedClusters, normalizedStandaloneMarkers]);
1017
+ const clusterLookup = React3__namespace.useMemo(() => {
1018
+ const lookup = /* @__PURE__ */ new Map();
1019
+ normalizedClusters.forEach((cluster) => {
1020
+ lookup.set(cluster.id, cluster);
1021
+ });
1022
+ return lookup;
1023
+ }, [normalizedClusters]);
1024
+ const firstCoordinate = React3__namespace.useMemo(() => {
1025
+ const allCoords = [];
1026
+ normalizedStandaloneMarkers.forEach((marker) => {
1027
+ allCoords.push({
1028
+ latitude: marker.latitude,
1029
+ longitude: marker.longitude
1030
+ });
1031
+ });
1032
+ normalizedClusters.forEach((cluster) => {
1033
+ allCoords.push({
1034
+ latitude: cluster.latitude,
1035
+ longitude: cluster.longitude
1036
+ });
1037
+ });
1038
+ if (allCoords.length > 0) {
1039
+ const sum = allCoords.reduce(
1040
+ (acc, coord) => ({
1041
+ latitude: acc.latitude + coord.latitude,
1042
+ longitude: acc.longitude + coord.longitude
1043
+ }),
1044
+ { latitude: 0, longitude: 0 }
1045
+ );
1046
+ return {
1047
+ latitude: sum.latitude / allCoords.length,
1048
+ longitude: sum.longitude / allCoords.length
1049
+ };
1050
+ }
1051
+ return {
1052
+ latitude: DEFAULT_VIEW_STATE.latitude,
1053
+ longitude: DEFAULT_VIEW_STATE.longitude
1054
+ };
1055
+ }, [normalizedClusters, normalizedStandaloneMarkers]);
1056
+ const calculatedZoom = React3__namespace.useMemo(() => {
1057
+ if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
1058
+ return markerFocusZoom;
1059
+ }
1060
+ const allCoords = [];
1061
+ normalizedStandaloneMarkers.forEach((marker) => {
1062
+ allCoords.push({
1063
+ latitude: marker.latitude,
1064
+ longitude: marker.longitude
1065
+ });
1066
+ });
1067
+ normalizedClusters.forEach((cluster) => {
1068
+ allCoords.push({
1069
+ latitude: cluster.latitude,
1070
+ longitude: cluster.longitude
1071
+ });
1072
+ });
1073
+ if (allCoords.length === 0) {
1074
+ return DEFAULT_VIEW_STATE.zoom;
1075
+ }
1076
+ const lats = allCoords.map((c) => c.latitude);
1077
+ const lngs = allCoords.map((c) => c.longitude);
1078
+ const latDiff = Math.max(...lats) - Math.min(...lats);
1079
+ const lngDiff = Math.max(...lngs) - Math.min(...lngs);
1080
+ const maxDiff = Math.max(latDiff, lngDiff);
1081
+ if (maxDiff > 10) return 3;
1082
+ if (maxDiff > 5) return 5;
1083
+ if (maxDiff > 2) return 7;
1084
+ if (maxDiff > 1) return 9;
1085
+ if (maxDiff > 0.5) return 10;
1086
+ if (maxDiff > 0.1) return 12;
1087
+ return 13;
1088
+ }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom]);
1089
+ const [uncontrolledViewState, setUncontrolledViewState] = React3__namespace.useState({
1090
+ latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
1091
+ longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
1092
+ zoom: defaultViewState?.zoom ?? calculatedZoom
1093
+ });
1094
+ React3__namespace.useEffect(() => {
1095
+ if (!viewState && !defaultViewState) {
1096
+ setUncontrolledViewState({
1097
+ latitude: firstCoordinate.latitude,
1098
+ longitude: firstCoordinate.longitude,
1099
+ zoom: calculatedZoom
1100
+ });
1101
+ }
1102
+ }, [firstCoordinate, calculatedZoom, viewState, defaultViewState]);
1103
+ const isControlledViewState = viewState !== void 0;
1104
+ const resolvedViewState = isControlledViewState ? viewState : uncontrolledViewState;
1105
+ const applyViewState = React3__namespace.useCallback(
1106
+ (nextState) => {
1107
+ if (!isControlledViewState) {
1108
+ setUncontrolledViewState((current) => {
1109
+ const next = { ...current, ...nextState };
1110
+ const hasChanged = current.latitude !== next.latitude || current.longitude !== next.longitude || current.zoom !== next.zoom;
1111
+ return hasChanged ? next : current;
1112
+ });
1113
+ }
1114
+ onViewStateChange?.(nextState);
1115
+ },
1116
+ [isControlledViewState, onViewStateChange]
1117
+ );
1118
+ const [selection, setSelection] = React3__namespace.useState(() => {
1119
+ if (initialSelectedMarkerId !== void 0 && initialSelectedMarkerId !== null) {
1120
+ return {
1121
+ type: "marker",
1122
+ markerId: String(initialSelectedMarkerId)
1123
+ };
1124
+ }
1125
+ return { type: "none" };
1126
+ });
1127
+ React3__namespace.useEffect(() => {
1128
+ if (selectedMarkerId === void 0 || selectedMarkerId === null) {
1129
+ return;
1130
+ }
1131
+ setSelection({
1132
+ type: "marker",
1133
+ markerId: String(selectedMarkerId)
1134
+ });
1135
+ }, [selectedMarkerId]);
1136
+ const selectedMarker = selection.markerId ? markerLookup.get(selection.markerId) : void 0;
1137
+ const selectedCluster = selection.clusterId ? clusterLookup.get(selection.clusterId) : void 0;
1138
+ React3__namespace.useEffect(() => {
1139
+ if (selection.type === "marker" && selection.markerId && !selectedMarker) {
1140
+ setSelection({ type: "none" });
1141
+ onSelectionChange?.({ type: "none" });
1142
+ }
1143
+ }, [onSelectionChange, selectedMarker, selection]);
1144
+ const emitSelectionChange = React3__namespace.useCallback(
1145
+ (nextSelection) => {
1146
+ if (nextSelection.type === "none") {
1147
+ onSelectionChange?.({ type: "none" });
1148
+ return;
1149
+ }
1150
+ if (nextSelection.type === "marker") {
1151
+ const parentCluster = nextSelection.marker.clusterId ? clusterLookup.get(nextSelection.marker.clusterId) : void 0;
1152
+ onSelectionChange?.({
1153
+ type: "marker",
1154
+ marker: nextSelection.marker,
1155
+ cluster: parentCluster
1156
+ });
1157
+ return;
1158
+ }
1159
+ onSelectionChange?.({
1160
+ type: "cluster",
1161
+ cluster: nextSelection.cluster
1162
+ });
1163
+ },
1164
+ [clusterLookup, onSelectionChange]
1165
+ );
1166
+ const selectMarker = React3__namespace.useCallback(
1167
+ (marker) => {
1168
+ setSelection({
1169
+ type: "marker",
1170
+ markerId: marker.id,
1171
+ clusterId: marker.clusterId
1172
+ });
1173
+ applyViewState({
1174
+ latitude: marker.latitude,
1175
+ longitude: marker.longitude,
1176
+ zoom: markerFocusZoom
1177
+ });
1178
+ emitSelectionChange({ type: "marker", marker });
1179
+ },
1180
+ [applyViewState, emitSelectionChange, markerFocusZoom]
1181
+ );
1182
+ const selectCluster = React3__namespace.useCallback(
1183
+ (cluster) => {
1184
+ setSelection({
1185
+ type: "cluster",
1186
+ clusterId: cluster.id
1187
+ });
1188
+ applyViewState({
1189
+ latitude: cluster.latitude,
1190
+ longitude: cluster.longitude,
1191
+ zoom: clusterFocusZoom
1192
+ });
1193
+ emitSelectionChange({ type: "cluster", cluster });
1194
+ },
1195
+ [applyViewState, clusterFocusZoom, emitSelectionChange]
1196
+ );
1197
+ const clearSelection = React3__namespace.useCallback(() => {
1198
+ setSelection({ type: "none" });
1199
+ emitSelectionChange({ type: "none" });
1200
+ }, [emitSelectionChange]);
1201
+ const mapMarkers = React3__namespace.useMemo(() => {
1202
+ const resolvedMarkers = [];
1203
+ normalizedClusters.forEach((cluster) => {
1204
+ const isSelected = selection.type === "cluster" && selection.clusterId === cluster.id;
1205
+ resolvedMarkers.push({
1206
+ id: `cluster-pin:${cluster.id}`,
1207
+ latitude: cluster.latitude,
1208
+ longitude: cluster.longitude,
1209
+ element: () => {
1210
+ const customMarkerElement = cluster.markerElement;
1211
+ const markerBody = typeof customMarkerElement === "function" ? customMarkerElement({
1212
+ isSelected,
1213
+ count: cluster.markers.length
1214
+ }) : customMarkerElement;
1215
+ return /* @__PURE__ */ jsxRuntime.jsx(
1216
+ "button",
1217
+ {
1218
+ type: "button",
1219
+ className: "group cursor-pointer",
1220
+ onClick: (event) => {
1221
+ event.preventDefault();
1222
+ event.stopPropagation();
1223
+ selectCluster(cluster);
1224
+ },
1225
+ "aria-label": `View ${cluster.markers.length} clustered locations`,
1226
+ children: markerBody ?? /* @__PURE__ */ jsxRuntime.jsx(
1227
+ "span",
1228
+ {
1229
+ className: cn(
1230
+ "inline-flex min-h-10 min-w-10 items-center justify-center rounded-full border-2 border-white px-2 text-xs font-semibold text-white shadow-lg transition-transform duration-200 group-hover:scale-105",
1231
+ isSelected && "ring-4 ring-primary/30",
1232
+ cluster.pinClassName
1233
+ ),
1234
+ style: {
1235
+ backgroundColor: cluster.pinColor ?? "var(--foreground)"
1236
+ },
1237
+ children: cluster.markers.length
1238
+ }
1239
+ )
1240
+ }
1241
+ );
1242
+ }
1243
+ });
1244
+ });
1245
+ normalizedStandaloneMarkers.forEach((marker) => {
1246
+ const isSelected = selection.type === "marker" && selection.markerId === marker.id;
1247
+ const customMarkerElement = marker.markerElement;
1248
+ resolvedMarkers.push({
1249
+ id: marker.id,
1250
+ latitude: marker.latitude,
1251
+ longitude: marker.longitude,
1252
+ draggable: marker.draggable,
1253
+ element: () => {
1254
+ const markerBody = typeof customMarkerElement === "function" ? customMarkerElement({ isSelected }) : customMarkerElement;
1255
+ return /* @__PURE__ */ jsxRuntime.jsx(
1256
+ "button",
1257
+ {
1258
+ type: "button",
1259
+ className: "group cursor-pointer",
1260
+ onClick: (event) => {
1261
+ event.preventDefault();
1262
+ event.stopPropagation();
1263
+ selectMarker(marker);
1264
+ },
1265
+ "aria-label": typeof marker.title === "string" ? `View ${marker.title}` : "View location details",
1266
+ children: markerBody ?? /* @__PURE__ */ jsxRuntime.jsx(
1267
+ "span",
1268
+ {
1269
+ className: cn(
1270
+ "inline-flex h-4 w-4 rounded-full border-2 border-white shadow-md transition-transform duration-200 group-hover:scale-110",
1271
+ isSelected && "h-5 w-5 ring-4 ring-primary/30",
1272
+ marker.pinClassName
1273
+ ),
1274
+ style: {
1275
+ backgroundColor: marker.pinColor ?? "#f43f5e"
1276
+ }
1277
+ }
1278
+ )
1279
+ }
1280
+ );
1281
+ }
1282
+ });
1283
+ });
1284
+ return resolvedMarkers;
1285
+ }, [
1286
+ normalizedClusters,
1287
+ normalizedStandaloneMarkers,
1288
+ selectCluster,
1289
+ selectMarker,
1290
+ selection
1291
+ ]);
1292
+ const renderMarkerPanel = () => {
1293
+ if (selectedMarker) {
1294
+ const markerMediaItems = selectedMarker.mediaItems ?? [];
1295
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1296
+ "div",
1297
+ {
1298
+ className: cn(
1299
+ "relative w-[320px] overflow-hidden rounded-xl border border-border bg-card text-card-foreground shadow-2xl",
1300
+ panelClassName
1301
+ ),
1302
+ children: [
1303
+ /* @__PURE__ */ jsxRuntime.jsx(
1304
+ "button",
1305
+ {
1306
+ type: "button",
1307
+ "aria-label": "Close marker details",
1308
+ className: "flex size-12 items-center justify-center rounded-bl-lg rounded-br-0 rounded-t-0 bg-black text-white transition-all duration-500 absolute top-0 right-0 z-10 cursor-pointer ring-4 ring-white",
1309
+ onClick: clearSelection,
1310
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { name: "lucide/x", size: 20 })
1311
+ }
1312
+ ),
1313
+ markerMediaItems.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1314
+ MarkerMediaCarousel,
1315
+ {
1316
+ mediaItems: markerMediaItems,
1317
+ optixFlowConfig,
1318
+ IconComponent,
1319
+ ImgComponent
1320
+ }
1321
+ ) : null,
1322
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 p-4", children: [
1323
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-1", children: [
1324
+ selectedMarker.eyebrow ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wide", children: selectedMarker.eyebrow }) : null,
1325
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold leading-tight", children: selectedMarker.title ?? selectedMarker.label ?? "Location" })
1326
+ ] }) }),
1327
+ selectedMarker.summary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm leading-relaxed", children: selectedMarker.summary }) : null,
1328
+ selectedMarker.locationLine ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-start text-sm gap-2", children: [
1329
+ /* @__PURE__ */ jsxRuntime.jsx(
1330
+ IconComponent,
1331
+ {
1332
+ name: "lucide:map-pin",
1333
+ className: "opacity-50",
1334
+ size: 18
1335
+ }
1336
+ ),
1337
+ typeof selectedMarker.locationLine === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1338
+ SimplePressable,
1339
+ {
1340
+ href: selectedMarker.locationUrl,
1341
+ className: cn(
1342
+ "transition-all duration-500",
1343
+ "font-medium opacity-75 hover:opacity-100",
1344
+ selectedMarker.locationUrl ? "underline underline-offset-4" : ""
1345
+ ),
1346
+ children: selectedMarker.locationLine
1347
+ }
1348
+ ) : selectedMarker.locationLine
1349
+ ] }) : null,
1350
+ selectedMarker.hoursLine ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-start text-sm gap-2", children: [
1351
+ /* @__PURE__ */ jsxRuntime.jsx(
1352
+ IconComponent,
1353
+ {
1354
+ name: "lucide:clock",
1355
+ className: "opacity-50",
1356
+ size: 18
1357
+ }
1358
+ ),
1359
+ typeof selectedMarker.hoursLine === "string" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: selectedMarker.hoursLine }) : selectedMarker.hoursLine
1360
+ ] }) : null,
1361
+ selectedMarker.markerContentComponent ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: selectedMarker.markerContentComponent }) : null,
1362
+ /* @__PURE__ */ jsxRuntime.jsx(MarkerActions, { actions: selectedMarker.actions })
1363
+ ] })
1364
+ ]
1365
+ }
1366
+ );
1367
+ }
1368
+ if (selectedCluster) {
1369
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1370
+ "div",
1371
+ {
1372
+ className: cn(
1373
+ "relative w-[320px] overflow-hidden rounded-xl border border-border bg-card text-card-foreground p-4 shadow-2xl",
1374
+ panelClassName
1375
+ ),
1376
+ children: [
1377
+ /* @__PURE__ */ jsxRuntime.jsx(
1378
+ "button",
1379
+ {
1380
+ type: "button",
1381
+ "aria-label": "Close cluster details",
1382
+ className: "flex size-8 items-center justify-center rounded-full border border-border bg-card text-card-foreground transition hover:bg-muted hover:text-foreground absolute top-2 right-2 z-10",
1383
+ onClick: clearSelection,
1384
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { name: "lucide/x", size: 20 })
1385
+ }
1386
+ ),
1387
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 flex items-start justify-between gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
1388
+ selectedCluster.label ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: selectedCluster.label }) : null,
1389
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold leading-tight text-foreground", children: selectedCluster.title ?? "Clustered Locations" }),
1390
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: selectedCluster.summary ?? `${selectedCluster.markers.length} location${selectedCluster.markers.length === 1 ? "" : "s"} in this cluster.` })
1391
+ ] }) }),
1392
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-56 space-y-2 overflow-y-auto pr-1", children: selectedCluster.markers.map((marker, markerIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
1393
+ "button",
1394
+ {
1395
+ type: "button",
1396
+ className: "w-full rounded-lg border border-border/60 p-3 text-left transition hover:border-border hover:bg-muted/50",
1397
+ onClick: () => selectMarker(marker),
1398
+ children: [
1399
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "line-clamp-1 text-sm font-semibold text-foreground", children: getMarkerTitle(marker, markerIndex) }),
1400
+ marker.summary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 line-clamp-2 text-xs text-muted-foreground", children: marker.summary }) : null
1401
+ ]
1402
+ },
1403
+ marker.id
1404
+ )) })
1405
+ ]
1406
+ }
1407
+ );
1408
+ }
1409
+ return null;
1410
+ };
1411
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1412
+ "div",
1413
+ {
1414
+ className: cn(
1415
+ "relative overflow-hidden rounded-2xl border border-border bg-background",
1416
+ className
1417
+ ),
1418
+ children: [
1419
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-[520px] w-full", mapWrapperClassName), children: /* @__PURE__ */ jsxRuntime.jsx(
1420
+ MapLibre,
1421
+ {
1422
+ stadiaApiKey,
1423
+ mapStyle,
1424
+ styleUrl,
1425
+ mapLibreCssHref,
1426
+ viewState: resolvedViewState,
1427
+ onViewStateChange: applyViewState,
1428
+ markers: mapMarkers,
1429
+ onClick: (coord) => {
1430
+ onMapClick?.(coord);
1431
+ if (clearSelectionOnMapClick) {
1432
+ clearSelection();
1433
+ }
1434
+ },
1435
+ onMarkerDrag,
1436
+ showNavigationControl,
1437
+ showGeolocateControl,
1438
+ navigationControlPosition,
1439
+ geolocateControlPosition,
1440
+ flyToOptions,
1441
+ className: cn("h-full w-full", mapClassName),
1442
+ children: mapChildren
1443
+ }
1444
+ ) }),
1445
+ selection.type !== "none" ? /* @__PURE__ */ jsxRuntime.jsx(
1446
+ "div",
1447
+ {
1448
+ className: cn(
1449
+ "pointer-events-none absolute z-20",
1450
+ PANEL_POSITION_CLASS[panelPosition]
1451
+ ),
1452
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: renderMarkerPanel() })
1453
+ }
1454
+ ) : null
1455
+ ]
1456
+ }
1457
+ );
1458
+ }
555
1459
 
556
1460
  exports.DTMapLibreMap = DTMapLibreMap;
1461
+ exports.GeoMap = GeoMap;
557
1462
  exports.MapLibre = MapLibre;
1463
+ exports.MapMarker = MapMarker;
1464
+ exports.NeutralMapMarker = NeutralMapMarker;
558
1465
  exports.appendStadiaApiKey = appendStadiaApiKey;
559
1466
  exports.computeDefaultZoom = computeDefaultZoom;
560
1467
  exports.computeGeoCenter = computeGeoCenter;
1468
+ exports.createMapMarkerElement = createMapMarkerElement;
561
1469
  exports.generateGoogleDirectionsLink = generateGoogleDirectionsLink;
562
1470
  exports.generateGoogleMapLink = generateGoogleMapLink;
563
1471
  exports.getMapLibreStyleUrl = getMapLibreStyleUrl;