@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.
@@ -658,6 +658,74 @@ function MapLibre({
658
658
  }
659
659
  );
660
660
  }
661
+ var TILE_SIZE = 512;
662
+ function latToMercatorY(lat) {
663
+ const latRad = lat * Math.PI / 180;
664
+ const mercN = Math.log(Math.tan(Math.PI / 4 + latRad / 2));
665
+ return TILE_SIZE / (2 * Math.PI) * (Math.PI - mercN);
666
+ }
667
+ function lngToMercatorX(lng) {
668
+ return TILE_SIZE / (2 * Math.PI) * ((lng + 180) / 360 * 2 * Math.PI);
669
+ }
670
+ function computeDefaultZoom(options) {
671
+ const {
672
+ coordinates,
673
+ mapWidth,
674
+ mapHeight,
675
+ padding = 50,
676
+ maxZoom = 18,
677
+ minZoom = 1
678
+ } = options;
679
+ if (coordinates.length === 0) return null;
680
+ if (coordinates.length === 1) return maxZoom;
681
+ if (mapWidth <= 0 || mapHeight <= 0) return null;
682
+ let minLat = Infinity;
683
+ let maxLat = -Infinity;
684
+ let minLng = Infinity;
685
+ let maxLng = -Infinity;
686
+ for (const coord of coordinates) {
687
+ if (coord.lat < minLat) minLat = coord.lat;
688
+ if (coord.lat > maxLat) maxLat = coord.lat;
689
+ if (coord.lng < minLng) minLng = coord.lng;
690
+ if (coord.lng > maxLng) maxLng = coord.lng;
691
+ }
692
+ const pixelXMin = lngToMercatorX(minLng);
693
+ const pixelXMax = lngToMercatorX(maxLng);
694
+ const pixelYMin = latToMercatorY(maxLat);
695
+ const pixelYMax = latToMercatorY(minLat);
696
+ const dx = Math.abs(pixelXMax - pixelXMin);
697
+ const dy = Math.abs(pixelYMax - pixelYMin);
698
+ const availableWidth = mapWidth - padding * 2;
699
+ const availableHeight = mapHeight - padding * 2;
700
+ if (availableWidth <= 0 || availableHeight <= 0) return minZoom;
701
+ let zoom;
702
+ if (dx === 0 && dy === 0) {
703
+ return maxZoom;
704
+ } else if (dx === 0) {
705
+ zoom = Math.log2(availableHeight / dy);
706
+ } else if (dy === 0) {
707
+ zoom = Math.log2(availableWidth / dx);
708
+ } else {
709
+ const zoomX = Math.log2(availableWidth / dx);
710
+ const zoomY = Math.log2(availableHeight / dy);
711
+ zoom = Math.min(zoomX, zoomY);
712
+ }
713
+ return Math.max(minZoom, Math.min(maxZoom, Math.floor(zoom * 100) / 100));
714
+ }
715
+ function useDefaultZoom(options) {
716
+ const { coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom } = options;
717
+ return React3.useMemo(
718
+ () => computeDefaultZoom({
719
+ coordinates,
720
+ mapWidth,
721
+ mapHeight,
722
+ padding,
723
+ maxZoom,
724
+ minZoom
725
+ }),
726
+ [coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom]
727
+ );
728
+ }
661
729
  var PANEL_POSITION_CLASS = {
662
730
  "top-left": "left-4 top-4",
663
731
  "top-right": "right-4 top-4",
@@ -906,6 +974,13 @@ function GeoMap({
906
974
  IconComponent = FallbackIcon,
907
975
  ImgComponent = FallbackImg
908
976
  }) {
977
+ const containerRef = React3__namespace.useRef(null);
978
+ const [containerDimensions, setContainerDimensions] = React3__namespace.useState({
979
+ width: 800,
980
+ // Default width
981
+ height: 520
982
+ // Default height
983
+ });
909
984
  const [isMobile, setIsMobile] = React3__namespace.useState(false);
910
985
  React3__namespace.useEffect(() => {
911
986
  const checkMobile = () => {
@@ -921,6 +996,24 @@ function GeoMap({
921
996
  }
922
997
  return isMobile ? 420 : 520;
923
998
  }, [mapSize, isMobile]);
999
+ React3__namespace.useEffect(() => {
1000
+ if (!containerRef.current) return;
1001
+ const updateDimensions = () => {
1002
+ if (containerRef.current) {
1003
+ const rect = containerRef.current.getBoundingClientRect();
1004
+ setContainerDimensions({
1005
+ width: rect.width || 800,
1006
+ height: rect.height || calculatedHeight
1007
+ });
1008
+ }
1009
+ };
1010
+ updateDimensions();
1011
+ if (typeof ResizeObserver !== "undefined") {
1012
+ const resizeObserver = new ResizeObserver(updateDimensions);
1013
+ resizeObserver.observe(containerRef.current);
1014
+ return () => resizeObserver.disconnect();
1015
+ }
1016
+ }, [calculatedHeight]);
924
1017
  const normalizedStandaloneMarkers = React3__namespace.useMemo(
925
1018
  () => markers.map((marker, index) => ({
926
1019
  ...marker,
@@ -1004,42 +1097,41 @@ function GeoMap({
1004
1097
  longitude: DEFAULT_VIEW_STATE.longitude
1005
1098
  };
1006
1099
  }, [normalizedClusters, normalizedStandaloneMarkers]);
1007
- const calculatedZoom = React3__namespace.useMemo(() => {
1008
- if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
1009
- return markerFocusZoom;
1010
- }
1011
- const allCoords = [];
1100
+ const zoomCoordinates = React3__namespace.useMemo(() => {
1101
+ const coords = [];
1012
1102
  normalizedStandaloneMarkers.forEach((marker) => {
1013
- allCoords.push({
1014
- latitude: marker.latitude,
1015
- longitude: marker.longitude
1103
+ coords.push({
1104
+ lat: marker.latitude,
1105
+ lng: marker.longitude
1016
1106
  });
1017
1107
  });
1018
1108
  normalizedClusters.forEach((cluster) => {
1019
- allCoords.push({
1020
- latitude: cluster.latitude,
1021
- longitude: cluster.longitude
1109
+ coords.push({
1110
+ lat: cluster.latitude,
1111
+ lng: cluster.longitude
1022
1112
  });
1023
1113
  });
1024
- if (allCoords.length === 0) {
1114
+ return coords;
1115
+ }, [normalizedStandaloneMarkers, normalizedClusters]);
1116
+ const properZoom = useDefaultZoom({
1117
+ coordinates: zoomCoordinates,
1118
+ mapWidth: containerDimensions.width,
1119
+ mapHeight: containerDimensions.height,
1120
+ padding: 80,
1121
+ // Increased padding for better framing
1122
+ maxZoom: 18,
1123
+ minZoom: 1
1124
+ });
1125
+ const calculatedZoom = React3__namespace.useMemo(() => {
1126
+ if (zoomCoordinates.length === 1) {
1127
+ return markerFocusZoom;
1128
+ }
1129
+ if (zoomCoordinates.length === 0) {
1025
1130
  return DEFAULT_VIEW_STATE.zoom;
1026
1131
  }
1027
- const lats = allCoords.map((c) => c.latitude);
1028
- const lngs = allCoords.map((c) => c.longitude);
1029
- const latDiff = Math.max(...lats) - Math.min(...lats);
1030
- const lngDiff = Math.max(...lngs) - Math.min(...lngs);
1031
- const maxDiff = Math.max(latDiff, lngDiff);
1032
- const heightFactor = calculatedHeight / 520;
1033
- const paddingFactor = 0.85;
1034
- if (maxDiff > 10) return Math.max(2, 3 * heightFactor * paddingFactor);
1035
- if (maxDiff > 5) return Math.max(4, 5 * heightFactor * paddingFactor);
1036
- if (maxDiff > 2) return Math.max(6, 7 * heightFactor * paddingFactor);
1037
- if (maxDiff > 1) return Math.max(8, 9 * heightFactor * paddingFactor);
1038
- if (maxDiff > 0.5) return Math.max(9, 10 * heightFactor * paddingFactor);
1039
- if (maxDiff > 0.1) return Math.max(11, 12 * heightFactor * paddingFactor);
1040
- if (maxDiff > 0.01) return Math.max(12, 13 * heightFactor * paddingFactor);
1041
- return Math.max(11, 12 * heightFactor * paddingFactor);
1042
- }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom, calculatedHeight]);
1132
+ const adjustedZoom = properZoom ? properZoom - 0.5 : DEFAULT_VIEW_STATE.zoom;
1133
+ return Math.min(adjustedZoom, 15);
1134
+ }, [properZoom, zoomCoordinates.length, markerFocusZoom]);
1043
1135
  const [uncontrolledViewState, setUncontrolledViewState] = React3__namespace.useState({
1044
1136
  latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
1045
1137
  longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
@@ -1365,6 +1457,7 @@ function GeoMap({
1365
1457
  return /* @__PURE__ */ jsxRuntime.jsxs(
1366
1458
  "div",
1367
1459
  {
1460
+ ref: containerRef,
1368
1461
  className: cn(
1369
1462
  "relative rounded-2xl border border-border bg-background",
1370
1463
  // Remove overflow-hidden from outer container to allow panel to overflow