@page-speed/maps 0.1.9 → 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",
@@ -787,6 +855,13 @@ function GeoMap({
787
855
  IconComponent = FallbackIcon,
788
856
  ImgComponent = FallbackImg
789
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
+ });
790
865
  const [isMobile, setIsMobile] = React3__namespace.useState(false);
791
866
  React3__namespace.useEffect(() => {
792
867
  const checkMobile = () => {
@@ -802,6 +877,24 @@ function GeoMap({
802
877
  }
803
878
  return isMobile ? 420 : 520;
804
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]);
805
898
  const normalizedStandaloneMarkers = React3__namespace.useMemo(
806
899
  () => markers.map((marker, index) => ({
807
900
  ...marker,
@@ -885,42 +978,41 @@ function GeoMap({
885
978
  longitude: DEFAULT_VIEW_STATE.longitude
886
979
  };
887
980
  }, [normalizedClusters, normalizedStandaloneMarkers]);
888
- const calculatedZoom = React3__namespace.useMemo(() => {
889
- if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
890
- return markerFocusZoom;
891
- }
892
- const allCoords = [];
981
+ const zoomCoordinates = React3__namespace.useMemo(() => {
982
+ const coords = [];
893
983
  normalizedStandaloneMarkers.forEach((marker) => {
894
- allCoords.push({
895
- latitude: marker.latitude,
896
- longitude: marker.longitude
984
+ coords.push({
985
+ lat: marker.latitude,
986
+ lng: marker.longitude
897
987
  });
898
988
  });
899
989
  normalizedClusters.forEach((cluster) => {
900
- allCoords.push({
901
- latitude: cluster.latitude,
902
- longitude: cluster.longitude
990
+ coords.push({
991
+ lat: cluster.latitude,
992
+ lng: cluster.longitude
903
993
  });
904
994
  });
905
- 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) {
906
1011
  return DEFAULT_VIEW_STATE.zoom;
907
1012
  }
908
- const lats = allCoords.map((c) => c.latitude);
909
- const lngs = allCoords.map((c) => c.longitude);
910
- const latDiff = Math.max(...lats) - Math.min(...lats);
911
- const lngDiff = Math.max(...lngs) - Math.min(...lngs);
912
- const maxDiff = Math.max(latDiff, lngDiff);
913
- const heightFactor = calculatedHeight / 520;
914
- const paddingFactor = 0.85;
915
- if (maxDiff > 10) return Math.max(2, 3 * heightFactor * paddingFactor);
916
- if (maxDiff > 5) return Math.max(4, 5 * heightFactor * paddingFactor);
917
- if (maxDiff > 2) return Math.max(6, 7 * heightFactor * paddingFactor);
918
- if (maxDiff > 1) return Math.max(8, 9 * heightFactor * paddingFactor);
919
- if (maxDiff > 0.5) return Math.max(9, 10 * heightFactor * paddingFactor);
920
- if (maxDiff > 0.1) return Math.max(11, 12 * heightFactor * paddingFactor);
921
- if (maxDiff > 0.01) return Math.max(12, 13 * heightFactor * paddingFactor);
922
- return Math.max(11, 12 * heightFactor * paddingFactor);
923
- }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom, calculatedHeight]);
1013
+ const adjustedZoom = properZoom ? properZoom - 0.5 : DEFAULT_VIEW_STATE.zoom;
1014
+ return Math.min(adjustedZoom, 15);
1015
+ }, [properZoom, zoomCoordinates.length, markerFocusZoom]);
924
1016
  const [uncontrolledViewState, setUncontrolledViewState] = React3__namespace.useState({
925
1017
  latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
926
1018
  longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
@@ -1246,6 +1338,7 @@ function GeoMap({
1246
1338
  return /* @__PURE__ */ jsxRuntime.jsxs(
1247
1339
  "div",
1248
1340
  {
1341
+ ref: containerRef,
1249
1342
  className: cn(
1250
1343
  "relative rounded-2xl border border-border bg-background",
1251
1344
  // Remove overflow-hidden from outer container to allow panel to overflow