@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.
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import React3__default from 'react';
2
+ import React3__default, { useMemo } from 'react';
3
3
  import { Marker, Map as Map$1, GeolocateControl, NavigationControl } from 'react-map-gl/maplibre';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
@@ -518,6 +518,74 @@ function MapLibre({
518
518
  }
519
519
  );
520
520
  }
521
+ var TILE_SIZE = 512;
522
+ function latToMercatorY(lat) {
523
+ const latRad = lat * Math.PI / 180;
524
+ const mercN = Math.log(Math.tan(Math.PI / 4 + latRad / 2));
525
+ return TILE_SIZE / (2 * Math.PI) * (Math.PI - mercN);
526
+ }
527
+ function lngToMercatorX(lng) {
528
+ return TILE_SIZE / (2 * Math.PI) * ((lng + 180) / 360 * 2 * Math.PI);
529
+ }
530
+ function computeDefaultZoom(options) {
531
+ const {
532
+ coordinates,
533
+ mapWidth,
534
+ mapHeight,
535
+ padding = 50,
536
+ maxZoom = 18,
537
+ minZoom = 1
538
+ } = options;
539
+ if (coordinates.length === 0) return null;
540
+ if (coordinates.length === 1) return maxZoom;
541
+ if (mapWidth <= 0 || mapHeight <= 0) return null;
542
+ let minLat = Infinity;
543
+ let maxLat = -Infinity;
544
+ let minLng = Infinity;
545
+ let maxLng = -Infinity;
546
+ for (const coord of coordinates) {
547
+ if (coord.lat < minLat) minLat = coord.lat;
548
+ if (coord.lat > maxLat) maxLat = coord.lat;
549
+ if (coord.lng < minLng) minLng = coord.lng;
550
+ if (coord.lng > maxLng) maxLng = coord.lng;
551
+ }
552
+ const pixelXMin = lngToMercatorX(minLng);
553
+ const pixelXMax = lngToMercatorX(maxLng);
554
+ const pixelYMin = latToMercatorY(maxLat);
555
+ const pixelYMax = latToMercatorY(minLat);
556
+ const dx = Math.abs(pixelXMax - pixelXMin);
557
+ const dy = Math.abs(pixelYMax - pixelYMin);
558
+ const availableWidth = mapWidth - padding * 2;
559
+ const availableHeight = mapHeight - padding * 2;
560
+ if (availableWidth <= 0 || availableHeight <= 0) return minZoom;
561
+ let zoom;
562
+ if (dx === 0 && dy === 0) {
563
+ return maxZoom;
564
+ } else if (dx === 0) {
565
+ zoom = Math.log2(availableHeight / dy);
566
+ } else if (dy === 0) {
567
+ zoom = Math.log2(availableWidth / dx);
568
+ } else {
569
+ const zoomX = Math.log2(availableWidth / dx);
570
+ const zoomY = Math.log2(availableHeight / dy);
571
+ zoom = Math.min(zoomX, zoomY);
572
+ }
573
+ return Math.max(minZoom, Math.min(maxZoom, Math.floor(zoom * 100) / 100));
574
+ }
575
+ function useDefaultZoom(options) {
576
+ const { coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom } = options;
577
+ return useMemo(
578
+ () => computeDefaultZoom({
579
+ coordinates,
580
+ mapWidth,
581
+ mapHeight,
582
+ padding,
583
+ maxZoom,
584
+ minZoom
585
+ }),
586
+ [coordinates, mapWidth, mapHeight, padding, maxZoom, minZoom]
587
+ );
588
+ }
521
589
  var PANEL_POSITION_CLASS = {
522
590
  "top-left": "left-4 top-4",
523
591
  "top-right": "right-4 top-4",
@@ -766,6 +834,13 @@ function GeoMap({
766
834
  IconComponent = FallbackIcon,
767
835
  ImgComponent = FallbackImg
768
836
  }) {
837
+ const containerRef = React3.useRef(null);
838
+ const [containerDimensions, setContainerDimensions] = React3.useState({
839
+ width: 800,
840
+ // Default width
841
+ height: 520
842
+ // Default height
843
+ });
769
844
  const [isMobile, setIsMobile] = React3.useState(false);
770
845
  React3.useEffect(() => {
771
846
  const checkMobile = () => {
@@ -781,6 +856,24 @@ function GeoMap({
781
856
  }
782
857
  return isMobile ? 420 : 520;
783
858
  }, [mapSize, isMobile]);
859
+ React3.useEffect(() => {
860
+ if (!containerRef.current) return;
861
+ const updateDimensions = () => {
862
+ if (containerRef.current) {
863
+ const rect = containerRef.current.getBoundingClientRect();
864
+ setContainerDimensions({
865
+ width: rect.width || 800,
866
+ height: rect.height || calculatedHeight
867
+ });
868
+ }
869
+ };
870
+ updateDimensions();
871
+ if (typeof ResizeObserver !== "undefined") {
872
+ const resizeObserver = new ResizeObserver(updateDimensions);
873
+ resizeObserver.observe(containerRef.current);
874
+ return () => resizeObserver.disconnect();
875
+ }
876
+ }, [calculatedHeight]);
784
877
  const normalizedStandaloneMarkers = React3.useMemo(
785
878
  () => markers.map((marker, index) => ({
786
879
  ...marker,
@@ -864,42 +957,41 @@ function GeoMap({
864
957
  longitude: DEFAULT_VIEW_STATE.longitude
865
958
  };
866
959
  }, [normalizedClusters, normalizedStandaloneMarkers]);
867
- const calculatedZoom = React3.useMemo(() => {
868
- if (normalizedStandaloneMarkers.length + normalizedClusters.length <= 1) {
869
- return markerFocusZoom;
870
- }
871
- const allCoords = [];
960
+ const zoomCoordinates = React3.useMemo(() => {
961
+ const coords = [];
872
962
  normalizedStandaloneMarkers.forEach((marker) => {
873
- allCoords.push({
874
- latitude: marker.latitude,
875
- longitude: marker.longitude
963
+ coords.push({
964
+ lat: marker.latitude,
965
+ lng: marker.longitude
876
966
  });
877
967
  });
878
968
  normalizedClusters.forEach((cluster) => {
879
- allCoords.push({
880
- latitude: cluster.latitude,
881
- longitude: cluster.longitude
969
+ coords.push({
970
+ lat: cluster.latitude,
971
+ lng: cluster.longitude
882
972
  });
883
973
  });
884
- if (allCoords.length === 0) {
974
+ return coords;
975
+ }, [normalizedStandaloneMarkers, normalizedClusters]);
976
+ const properZoom = useDefaultZoom({
977
+ coordinates: zoomCoordinates,
978
+ mapWidth: containerDimensions.width,
979
+ mapHeight: containerDimensions.height,
980
+ padding: 80,
981
+ // Increased padding for better framing
982
+ maxZoom: 18,
983
+ minZoom: 1
984
+ });
985
+ const calculatedZoom = React3.useMemo(() => {
986
+ if (zoomCoordinates.length === 1) {
987
+ return markerFocusZoom;
988
+ }
989
+ if (zoomCoordinates.length === 0) {
885
990
  return DEFAULT_VIEW_STATE.zoom;
886
991
  }
887
- const lats = allCoords.map((c) => c.latitude);
888
- const lngs = allCoords.map((c) => c.longitude);
889
- const latDiff = Math.max(...lats) - Math.min(...lats);
890
- const lngDiff = Math.max(...lngs) - Math.min(...lngs);
891
- const maxDiff = Math.max(latDiff, lngDiff);
892
- const heightFactor = calculatedHeight / 520;
893
- const paddingFactor = 0.85;
894
- if (maxDiff > 10) return Math.max(2, 3 * heightFactor * paddingFactor);
895
- if (maxDiff > 5) return Math.max(4, 5 * heightFactor * paddingFactor);
896
- if (maxDiff > 2) return Math.max(6, 7 * heightFactor * paddingFactor);
897
- if (maxDiff > 1) return Math.max(8, 9 * heightFactor * paddingFactor);
898
- if (maxDiff > 0.5) return Math.max(9, 10 * heightFactor * paddingFactor);
899
- if (maxDiff > 0.1) return Math.max(11, 12 * heightFactor * paddingFactor);
900
- if (maxDiff > 0.01) return Math.max(12, 13 * heightFactor * paddingFactor);
901
- return Math.max(11, 12 * heightFactor * paddingFactor);
902
- }, [normalizedClusters, normalizedStandaloneMarkers, markerFocusZoom, calculatedHeight]);
992
+ const adjustedZoom = properZoom ? properZoom - 0.5 : DEFAULT_VIEW_STATE.zoom;
993
+ return Math.min(adjustedZoom, 15);
994
+ }, [properZoom, zoomCoordinates.length, markerFocusZoom]);
903
995
  const [uncontrolledViewState, setUncontrolledViewState] = React3.useState({
904
996
  latitude: defaultViewState?.latitude ?? firstCoordinate.latitude,
905
997
  longitude: defaultViewState?.longitude ?? firstCoordinate.longitude,
@@ -1225,6 +1317,7 @@ function GeoMap({
1225
1317
  return /* @__PURE__ */ jsxs(
1226
1318
  "div",
1227
1319
  {
1320
+ ref: containerRef,
1228
1321
  className: cn(
1229
1322
  "relative rounded-2xl border border-border bg-background",
1230
1323
  // Remove overflow-hidden from outer container to allow panel to overflow