@page-speed/maps 0.1.8 → 0.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.
@@ -539,6 +539,74 @@ function MapLibre({
539
539
  }
540
540
  );
541
541
  }
542
+ var TILE_SIZE = 512;
543
+ function latToMercatorY(lat) {
544
+ const latRad = lat * Math.PI / 180;
545
+ const mercN = Math.log(Math.tan(Math.PI / 4 + latRad / 2));
546
+ return TILE_SIZE / (2 * Math.PI) * (Math.PI - mercN);
547
+ }
548
+ function lngToMercatorX(lng) {
549
+ return TILE_SIZE / (2 * Math.PI) * ((lng + 180) / 360 * 2 * Math.PI);
550
+ }
551
+ function computeDefaultZoom(options) {
552
+ const {
553
+ coordinates,
554
+ mapWidth,
555
+ mapHeight,
556
+ padding = 50,
557
+ maxZoom = 18,
558
+ minZoom = 1
559
+ } = options;
560
+ if (coordinates.length === 0) return null;
561
+ if (coordinates.length === 1) return maxZoom;
562
+ if (mapWidth <= 0 || mapHeight <= 0) return null;
563
+ let minLat = Infinity;
564
+ let maxLat = -Infinity;
565
+ let minLng = Infinity;
566
+ let maxLng = -Infinity;
567
+ for (const coord of coordinates) {
568
+ if (coord.lat < minLat) minLat = coord.lat;
569
+ if (coord.lat > maxLat) maxLat = coord.lat;
570
+ if (coord.lng < minLng) minLng = coord.lng;
571
+ if (coord.lng > maxLng) maxLng = coord.lng;
572
+ }
573
+ const pixelXMin = lngToMercatorX(minLng);
574
+ const pixelXMax = lngToMercatorX(maxLng);
575
+ const pixelYMin = latToMercatorY(maxLat);
576
+ const pixelYMax = latToMercatorY(minLat);
577
+ const dx = Math.abs(pixelXMax - pixelXMin);
578
+ const dy = Math.abs(pixelYMax - pixelYMin);
579
+ const availableWidth = mapWidth - padding * 2;
580
+ const availableHeight = mapHeight - padding * 2;
581
+ if (availableWidth <= 0 || availableHeight <= 0) return minZoom;
582
+ let zoom;
583
+ if (dx === 0 && dy === 0) {
584
+ return maxZoom;
585
+ } else if (dx === 0) {
586
+ zoom = Math.log2(availableHeight / dy);
587
+ } else if (dy === 0) {
588
+ zoom = Math.log2(availableWidth / dx);
589
+ } else {
590
+ const zoomX = Math.log2(availableWidth / dx);
591
+ const zoomY = Math.log2(availableHeight / dy);
592
+ zoom = Math.min(zoomX, zoomY);
593
+ }
594
+ return Math.max(minZoom, Math.min(maxZoom, Math.floor(zoom * 100) / 100));
595
+ }
596
+ function useDefaultZoom(options) {
597
+ const { coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom } = options;
598
+ return React3.useMemo(
599
+ () => computeDefaultZoom({
600
+ coordinates,
601
+ mapWidth,
602
+ mapHeight,
603
+ padding,
604
+ maxZoom,
605
+ minZoom
606
+ }),
607
+ [coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom]
608
+ );
609
+ }
542
610
  var PANEL_POSITION_CLASS = {
543
611
  "top-left": "left-4 top-4",
544
612
  "top-right": "right-4 top-4",
@@ -783,9 +851,50 @@ function GeoMap({
783
851
  clearSelectionOnMapClick = true,
784
852
  mapChildren,
785
853
  optixFlowConfig,
854
+ mapSize,
786
855
  IconComponent = FallbackIcon,
787
856
  ImgComponent = FallbackImg
788
857
  }) {
858
+ const containerRef = React3__namespace.useRef(null);
859
+ const [containerDimensions, setContainerDimensions] = React3__namespace.useState({
860
+ width: 800,
861
+ // Default width
862
+ height: 520
863
+ // Default height
864
+ });
865
+ const [isMobile, setIsMobile] = React3__namespace.useState(false);
866
+ React3__namespace.useEffect(() => {
867
+ const checkMobile = () => {
868
+ setIsMobile(window.innerWidth < 768);
869
+ };
870
+ checkMobile();
871
+ window.addEventListener("resize", checkMobile);
872
+ return () => window.removeEventListener("resize", checkMobile);
873
+ }, []);
874
+ const calculatedHeight = React3__namespace.useMemo(() => {
875
+ if (mapSize) {
876
+ return isMobile ? mapSize.mobile : mapSize.desktop;
877
+ }
878
+ return isMobile ? 420 : 520;
879
+ }, [mapSize, isMobile]);
880
+ React3__namespace.useEffect(() => {
881
+ if (!containerRef.current) return;
882
+ const updateDimensions = () => {
883
+ if (containerRef.current) {
884
+ const rect = containerRef.current.getBoundingClientRect();
885
+ setContainerDimensions({
886
+ width: rect.width || 800,
887
+ height: rect.height || calculatedHeight
888
+ });
889
+ }
890
+ };
891
+ updateDimensions();
892
+ if (typeof ResizeObserver !== "undefined") {
893
+ const resizeObserver = new ResizeObserver(updateDimensions);
894
+ resizeObserver.observe(containerRef.current);
895
+ return () => resizeObserver.disconnect();
896
+ }
897
+ }, [calculatedHeight]);
789
898
  const normalizedStandaloneMarkers = React3__namespace.useMemo(
790
899
  () => markers.map((marker, index) => ({
791
900
  ...marker,
@@ -869,39 +978,41 @@ function GeoMap({
869
978
  longitude: DEFAULT_VIEW_STATE.longitude
870
979
  };
871
980
  }, [normalizedClusters, normalizedStandaloneMarkers]);
872
- const calculatedZoom = React3__namespace.useMemo(() => {
873
- if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
874
- return markerFocusZoom;
875
- }
876
- const allCoords = [];
981
+ const zoomCoordinates = React3__namespace.useMemo(() => {
982
+ const coords = [];
877
983
  normalizedStandaloneMarkers.forEach((marker) => {
878
- allCoords.push({
879
- latitude: marker.latitude,
880
- longitude: marker.longitude
984
+ coords.push({
985
+ lat: marker.latitude,
986
+ lng: marker.longitude
881
987
  });
882
988
  });
883
989
  normalizedClusters.forEach((cluster) => {
884
- allCoords.push({
885
- latitude: cluster.latitude,
886
- longitude: cluster.longitude
990
+ coords.push({
991
+ lat: cluster.latitude,
992
+ lng: cluster.longitude
887
993
  });
888
994
  });
889
- if (allCoords.length === 0) {
995
+ return coords;
996
+ }, [normalizedStandaloneMarkers, normalizedClusters]);
997
+ const properZoom = useDefaultZoom({
998
+ coordinates: zoomCoordinates,
999
+ mapWidth: containerDimensions.width,
1000
+ mapHeight: containerDimensions.height,
1001
+ padding: 80,
1002
+ // Increased padding for better framing
1003
+ maxZoom: 18,
1004
+ minZoom: 1
1005
+ });
1006
+ const calculatedZoom = React3__namespace.useMemo(() => {
1007
+ if (zoomCoordinates.length === 1) {
1008
+ return markerFocusZoom;
1009
+ }
1010
+ if (zoomCoordinates.length === 0) {
890
1011
  return DEFAULT_VIEW_STATE.zoom;
891
1012
  }
892
- const lats = allCoords.map((c) => c.latitude);
893
- const lngs = allCoords.map((c) => c.longitude);
894
- const latDiff = Math.max(...lats) - Math.min(...lats);
895
- const lngDiff = Math.max(...lngs) - Math.min(...lngs);
896
- const maxDiff = Math.max(latDiff, lngDiff);
897
- if (maxDiff > 10) return 3;
898
- if (maxDiff > 5) return 5;
899
- if (maxDiff > 2) return 7;
900
- if (maxDiff > 1) return 9;
901
- if (maxDiff > 0.5) return 10;
902
- if (maxDiff > 0.1) return 12;
903
- return 13;
904
- }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom]);
1013
+ const adjustedZoom = properZoom ? properZoom - 0.5 : DEFAULT_VIEW_STATE.zoom;
1014
+ return Math.min(adjustedZoom, 15);
1015
+ }, [properZoom, zoomCoordinates.length, markerFocusZoom]);
905
1016
  const [uncontrolledViewState, setUncontrolledViewState] = React3__namespace.useState({
906
1017
  latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
907
1018
  longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
@@ -1227,25 +1338,35 @@ function GeoMap({
1227
1338
  return /* @__PURE__ */ jsxRuntime.jsxs(
1228
1339
  "div",
1229
1340
  {
1341
+ ref: containerRef,
1230
1342
  className: cn(
1231
- "relative overflow-hidden rounded-2xl border border-border bg-background",
1343
+ "relative rounded-2xl border border-border bg-background",
1344
+ // Remove overflow-hidden from outer container to allow panel to overflow
1232
1345
  className
1233
1346
  ),
1347
+ style: {
1348
+ // If className includes height settings, they'll override via CSS specificity
1349
+ height: className?.includes("h-[") || className?.includes("min-h-[") || className?.includes("max-h-[") ? void 0 : `${calculatedHeight}px`,
1350
+ // Explicitly allow overflow for marker panels
1351
+ overflow: "visible"
1352
+ },
1234
1353
  children: [
1235
1354
  /* @__PURE__ */ jsxRuntime.jsx(
1236
1355
  "div",
1237
1356
  {
1238
1357
  className: cn(
1239
- "w-full",
1240
- // Default height, can be overridden by mapWrapperClassName
1241
- mapWrapperClassName || "h-[520px]"
1358
+ "w-full rounded-2xl",
1359
+ // Only apply default height class if mapWrapperClassName not provided
1360
+ !mapWrapperClassName && `h-[${calculatedHeight}px]`,
1361
+ mapWrapperClassName
1242
1362
  ),
1243
1363
  style: {
1244
- // CRITICAL: Always use explicit height to prevent MapLibre canvas expansion
1245
- height: mapWrapperClassName ? void 0 : "520px",
1364
+ // If mapWrapperClassName includes height, let it handle the height
1365
+ height: mapWrapperClassName?.includes("h-[") || mapWrapperClassName?.includes("min-h-[") || mapWrapperClassName?.includes("max-h-[") ? void 0 : `${calculatedHeight}px`,
1246
1366
  maxHeight: "100vh",
1247
1367
  // Prevent excessive growth
1248
1368
  position: "relative",
1369
+ // Keep overflow hidden only on the map wrapper to contain the canvas
1249
1370
  overflow: "hidden"
1250
1371
  },
1251
1372
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1271,6 +1392,10 @@ function GeoMap({
1271
1392
  geolocateControlPosition,
1272
1393
  flyToOptions,
1273
1394
  className: cn("h-full w-full", mapClassName),
1395
+ style: {
1396
+ // Pass the calculated height to MapLibre for better zoom calculations
1397
+ height: mapClassName?.includes("h-[") || mapClassName?.includes("min-h-[") || mapClassName?.includes("max-h-[") ? void 0 : `${calculatedHeight}px`
1398
+ },
1274
1399
  children: mapChildren
1275
1400
  }
1276
1401
  )
@@ -1280,9 +1405,13 @@ function GeoMap({
1280
1405
  "div",
1281
1406
  {
1282
1407
  className: cn(
1283
- "pointer-events-none absolute z-20",
1408
+ "pointer-events-none absolute z-30",
1284
1409
  PANEL_POSITION_CLASS[panelPosition]
1285
1410
  ),
1411
+ style: {
1412
+ // Ensure panel can overflow and has higher z-index
1413
+ zIndex: 30
1414
+ },
1286
1415
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: renderMarkerPanel() })
1287
1416
  }
1288
1417
  ) : null