@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.
@@ -2,7 +2,7 @@ import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import * as React3 from 'react';
5
- import React3__default from 'react';
5
+ import React3__default, { useMemo } from 'react';
6
6
  import { Marker, Map as Map$1, GeolocateControl, NavigationControl } from 'react-map-gl/maplibre';
7
7
 
8
8
  // src/utils/cn.ts
@@ -637,6 +637,74 @@ function MapLibre({
637
637
  }
638
638
  );
639
639
  }
640
+ var TILE_SIZE = 512;
641
+ function latToMercatorY(lat) {
642
+ const latRad = lat * Math.PI / 180;
643
+ const mercN = Math.log(Math.tan(Math.PI / 4 + latRad / 2));
644
+ return TILE_SIZE / (2 * Math.PI) * (Math.PI - mercN);
645
+ }
646
+ function lngToMercatorX(lng) {
647
+ return TILE_SIZE / (2 * Math.PI) * ((lng + 180) / 360 * 2 * Math.PI);
648
+ }
649
+ function computeDefaultZoom(options) {
650
+ const {
651
+ coordinates,
652
+ mapWidth,
653
+ mapHeight,
654
+ padding = 50,
655
+ maxZoom = 18,
656
+ minZoom = 1
657
+ } = options;
658
+ if (coordinates.length === 0) return null;
659
+ if (coordinates.length === 1) return maxZoom;
660
+ if (mapWidth <= 0 || mapHeight <= 0) return null;
661
+ let minLat = Infinity;
662
+ let maxLat = -Infinity;
663
+ let minLng = Infinity;
664
+ let maxLng = -Infinity;
665
+ for (const coord of coordinates) {
666
+ if (coord.lat < minLat) minLat = coord.lat;
667
+ if (coord.lat > maxLat) maxLat = coord.lat;
668
+ if (coord.lng < minLng) minLng = coord.lng;
669
+ if (coord.lng > maxLng) maxLng = coord.lng;
670
+ }
671
+ const pixelXMin = lngToMercatorX(minLng);
672
+ const pixelXMax = lngToMercatorX(maxLng);
673
+ const pixelYMin = latToMercatorY(maxLat);
674
+ const pixelYMax = latToMercatorY(minLat);
675
+ const dx = Math.abs(pixelXMax - pixelXMin);
676
+ const dy = Math.abs(pixelYMax - pixelYMin);
677
+ const availableWidth = mapWidth - padding * 2;
678
+ const availableHeight = mapHeight - padding * 2;
679
+ if (availableWidth <= 0 || availableHeight <= 0) return minZoom;
680
+ let zoom;
681
+ if (dx === 0 && dy === 0) {
682
+ return maxZoom;
683
+ } else if (dx === 0) {
684
+ zoom = Math.log2(availableHeight / dy);
685
+ } else if (dy === 0) {
686
+ zoom = Math.log2(availableWidth / dx);
687
+ } else {
688
+ const zoomX = Math.log2(availableWidth / dx);
689
+ const zoomY = Math.log2(availableHeight / dy);
690
+ zoom = Math.min(zoomX, zoomY);
691
+ }
692
+ return Math.max(minZoom, Math.min(maxZoom, Math.floor(zoom * 100) / 100));
693
+ }
694
+ function useDefaultZoom(options) {
695
+ const { coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom } = options;
696
+ return useMemo(
697
+ () => computeDefaultZoom({
698
+ coordinates,
699
+ mapWidth,
700
+ mapHeight,
701
+ padding,
702
+ maxZoom,
703
+ minZoom
704
+ }),
705
+ [coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom]
706
+ );
707
+ }
640
708
  var PANEL_POSITION_CLASS = {
641
709
  "top-left": "left-4 top-4",
642
710
  "top-right": "right-4 top-4",
@@ -885,6 +953,13 @@ function GeoMap({
885
953
  IconComponent = FallbackIcon,
886
954
  ImgComponent = FallbackImg
887
955
  }) {
956
+ const containerRef = React3.useRef(null);
957
+ const [containerDimensions, setContainerDimensions] = React3.useState({
958
+ width: 800,
959
+ // Default width
960
+ height: 520
961
+ // Default height
962
+ });
888
963
  const [isMobile, setIsMobile] = React3.useState(false);
889
964
  React3.useEffect(() => {
890
965
  const checkMobile = () => {
@@ -900,6 +975,24 @@ function GeoMap({
900
975
  }
901
976
  return isMobile ? 420 : 520;
902
977
  }, [mapSize, isMobile]);
978
+ React3.useEffect(() => {
979
+ if (!containerRef.current) return;
980
+ const updateDimensions = () => {
981
+ if (containerRef.current) {
982
+ const rect = containerRef.current.getBoundingClientRect();
983
+ setContainerDimensions({
984
+ width: rect.width || 800,
985
+ height: rect.height || calculatedHeight
986
+ });
987
+ }
988
+ };
989
+ updateDimensions();
990
+ if (typeof ResizeObserver !== "undefined") {
991
+ const resizeObserver = new ResizeObserver(updateDimensions);
992
+ resizeObserver.observe(containerRef.current);
993
+ return () => resizeObserver.disconnect();
994
+ }
995
+ }, [calculatedHeight]);
903
996
  const normalizedStandaloneMarkers = React3.useMemo(
904
997
  () => markers.map((marker, index) => ({
905
998
  ...marker,
@@ -983,42 +1076,41 @@ function GeoMap({
983
1076
  longitude: DEFAULT_VIEW_STATE.longitude
984
1077
  };
985
1078
  }, [normalizedClusters, normalizedStandaloneMarkers]);
986
- const calculatedZoom = React3.useMemo(() => {
987
- if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
988
- return markerFocusZoom;
989
- }
990
- const allCoords = [];
1079
+ const zoomCoordinates = React3.useMemo(() => {
1080
+ const coords = [];
991
1081
  normalizedStandaloneMarkers.forEach((marker) => {
992
- allCoords.push({
993
- latitude: marker.latitude,
994
- longitude: marker.longitude
1082
+ coords.push({
1083
+ lat: marker.latitude,
1084
+ lng: marker.longitude
995
1085
  });
996
1086
  });
997
1087
  normalizedClusters.forEach((cluster) => {
998
- allCoords.push({
999
- latitude: cluster.latitude,
1000
- longitude: cluster.longitude
1088
+ coords.push({
1089
+ lat: cluster.latitude,
1090
+ lng: cluster.longitude
1001
1091
  });
1002
1092
  });
1003
- if (allCoords.length === 0) {
1093
+ return coords;
1094
+ }, [normalizedStandaloneMarkers, normalizedClusters]);
1095
+ const properZoom = useDefaultZoom({
1096
+ coordinates: zoomCoordinates,
1097
+ mapWidth: containerDimensions.width,
1098
+ mapHeight: containerDimensions.height,
1099
+ padding: 80,
1100
+ // Increased padding for better framing
1101
+ maxZoom: 18,
1102
+ minZoom: 1
1103
+ });
1104
+ const calculatedZoom = React3.useMemo(() => {
1105
+ if (zoomCoordinates.length === 1) {
1106
+ return markerFocusZoom;
1107
+ }
1108
+ if (zoomCoordinates.length === 0) {
1004
1109
  return DEFAULT_VIEW_STATE.zoom;
1005
1110
  }
1006
- const lats = allCoords.map((c) => c.latitude);
1007
- const lngs = allCoords.map((c) => c.longitude);
1008
- const latDiff = Math.max(...lats) - Math.min(...lats);
1009
- const lngDiff = Math.max(...lngs) - Math.min(...lngs);
1010
- const maxDiff = Math.max(latDiff, lngDiff);
1011
- const heightFactor = calculatedHeight / 520;
1012
- const paddingFactor = 0.85;
1013
- if (maxDiff > 10) return Math.max(2, 3 * heightFactor * paddingFactor);
1014
- if (maxDiff > 5) return Math.max(4, 5 * heightFactor * paddingFactor);
1015
- if (maxDiff > 2) return Math.max(6, 7 * heightFactor * paddingFactor);
1016
- if (maxDiff > 1) return Math.max(8, 9 * heightFactor * paddingFactor);
1017
- if (maxDiff > 0.5) return Math.max(9, 10 * heightFactor * paddingFactor);
1018
- if (maxDiff > 0.1) return Math.max(11, 12 * heightFactor * paddingFactor);
1019
- if (maxDiff > 0.01) return Math.max(12, 13 * heightFactor * paddingFactor);
1020
- return Math.max(11, 12 * heightFactor * paddingFactor);
1021
- }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom, calculatedHeight]);
1111
+ const adjustedZoom = properZoom ? properZoom - 0.5 : DEFAULT_VIEW_STATE.zoom;
1112
+ return Math.min(adjustedZoom, 15);
1113
+ }, [properZoom, zoomCoordinates.length, markerFocusZoom]);
1022
1114
  const [uncontrolledViewState, setUncontrolledViewState] = React3.useState({
1023
1115
  latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
1024
1116
  longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
@@ -1344,6 +1436,7 @@ function GeoMap({
1344
1436
  return /* @__PURE__ */ jsxs(
1345
1437
  "div",
1346
1438
  {
1439
+ ref: containerRef,
1347
1440
  className: cn(
1348
1441
  "relative rounded-2xl border border-border bg-background",
1349
1442
  // Remove overflow-hidden from outer container to allow panel to overflow