Grouping markers and changing pin to tear shape

This commit is contained in:
jackiettran
2025-12-30 16:58:03 -05:00
parent 6cf8a009ff
commit 4bb4e7bcb6
2 changed files with 93 additions and 75 deletions

View File

@@ -21,12 +21,6 @@ const ItemMarkerInfo: React.FC<ItemMarkerInfoProps> = ({ item, onViewDetails })
return 'Contact for pricing';
};
const getLocationDisplay = () => {
return item.city && item.state
? `${item.city}, ${item.state}`
: 'Location not specified';
};
return (
<div style={{ width: 'min(280px, 90vw)', maxWidth: '280px' }}>
<div className="card border-0">
@@ -55,18 +49,14 @@ const ItemMarkerInfo: React.FC<ItemMarkerInfoProps> = ({ item, onViewDetails })
)}
<div className="card-body p-3">
<h6 className="card-title mb-2 text-dark fw-bold text-truncate">
<div className="card-title mb-2 text-dark fw-bold text-truncate" style={{ fontSize: '0.875rem' }}>
{item.name}
</h6>
<div className="mb-2">
<span className="text-primary fw-bold">
{getPriceDisplay()}
</span>
</div>
<div className="text-muted small mb-2">
<i className="bi bi-geo-alt"></i> {getLocationDisplay()}
<div className="mb-2">
<span className="text-primary fw-semibold" style={{ fontSize: '0.8rem' }}>
{getPriceDisplay()}
</span>
</div>
{item.description && (

View File

@@ -59,53 +59,59 @@ const SearchResultsMap: React.FC<SearchResultsMapProps> = ({
}
}, []);
// Create marker for an item
const createItemMarker = useCallback(
async (item: Item, map: google.maps.Map) => {
if (!item.latitude || !item.longitude) return null;
// Group items by location
const groupItemsByLocation = useCallback((items: Item[]) => {
const groups: Map<string, Item[]> = new Map();
const { AdvancedMarkerElement } = (await google.maps.importLibrary(
items.forEach((item) => {
if (item.latitude && item.longitude) {
const key = `${item.latitude},${item.longitude}`;
const existing = groups.get(key) || [];
existing.push(item);
groups.set(key, existing);
}
});
return groups;
}, []);
// Create marker for a group of items at the same location
const createLocationMarker = useCallback(
async (items: Item[], map: google.maps.Map) => {
if (items.length === 0 || !items[0].latitude || !items[0].longitude) return null;
const { AdvancedMarkerElement, PinElement } =
(await google.maps.importLibrary(
"marker"
)) as google.maps.MarkerLibrary;
// Create marker element
const itemCount = items.length;
const isMobile = window.innerWidth < 768;
const markerSize = isMobile ? 48 : 40;
const markerElement = document.createElement("div");
markerElement.className =
"d-flex align-items-center justify-content-center";
markerElement.style.cssText = `
width: ${markerSize}px;
height: ${markerSize}px;
background-color: #0d6efd;
border: 3px solid white;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
transition: transform 0.2s ease;
touch-action: manipulation;
`;
// Add icon
const icon = document.createElement("i");
icon.className = "bi bi-geo-fill text-white";
icon.style.fontSize = "16px";
markerElement.appendChild(icon);
// Hover effects
markerElement.addEventListener("mouseenter", () => {
markerElement.style.transform = "scale(1.1)";
// Create teardrop pin marker with badge for multiple items
const pin = new PinElement({
background: "#0d6efd",
borderColor: "#ffffff",
glyphColor: "#ffffff",
scale: isMobile ? 1.2 : 1.0,
glyph: itemCount > 1 ? `${itemCount}` : undefined,
});
markerElement.addEventListener("mouseleave", () => {
markerElement.style.transform = "scale(1)";
// Add hover effects
pin.element.style.cursor = "pointer";
pin.element.style.transition = "transform 0.2s ease";
pin.element.addEventListener("mouseenter", () => {
pin.element.style.transform = "scale(1.1)";
});
pin.element.addEventListener("mouseleave", () => {
pin.element.style.transform = "scale(1)";
});
const marker = new AdvancedMarkerElement({
position: { lat: item.latitude, lng: item.longitude },
position: { lat: items[0].latitude, lng: items[0].longitude },
map: map,
content: markerElement,
title: item.name,
content: pin.element,
title: itemCount > 1 ? `${itemCount} items` : items[0].name,
});
// Add click listener
@@ -118,16 +124,36 @@ const SearchResultsMap: React.FC<SearchResultsMapProps> = ({
headerDisabled: true,
maxWidth:
window.innerWidth < 768
? Math.min(300, window.innerWidth - 40)
: 300,
? Math.min(320, window.innerWidth - 40)
: 320,
});
// Create React container for info window content
const infoContainer = document.createElement("div");
const root = createRoot(infoContainer);
if (itemCount === 1) {
// Single item - show normal info
root.render(
<ItemMarkerInfo
item={items[0]}
onViewDetails={() => {
infoWindow.close();
if (onItemSelect) {
onItemSelect(items[0]);
} else {
window.location.href = `/items/${items[0].id}`;
}
}}
/>
);
} else {
// Multiple items - show list
root.render(
<div style={{ width: 'min(300px, 85vw)', maxHeight: '350px', overflowY: 'auto' }}>
{items.map((item) => (
<ItemMarkerInfo
key={item.id}
item={item}
onViewDetails={() => {
infoWindow.close();
@@ -138,7 +164,10 @@ const SearchResultsMap: React.FC<SearchResultsMapProps> = ({
}
}}
/>
))}
</div>
);
}
infoWindow.setContent(infoContainer);
infoWindow.open(map, marker);
@@ -150,23 +179,22 @@ const SearchResultsMap: React.FC<SearchResultsMapProps> = ({
[onItemSelect]
);
// Add markers for all items
// Add markers for all items (grouped by location)
const addMarkersToMap = useCallback(
async (map: google.maps.Map, items: Item[]) => {
clearMarkers();
const validItems = items.filter(
(item) => item.latitude && item.longitude
);
const locationGroups = groupItemsByLocation(items);
for (const item of validItems) {
const marker = await createItemMarker(item, map);
const groupsArray = Array.from(locationGroups.values());
for (const groupItems of groupsArray) {
const marker = await createLocationMarker(groupItems, map);
if (marker) {
markersRef.current.push(marker);
}
}
},
[createItemMarker, clearMarkers]
[createLocationMarker, clearMarkers, groupItemsByLocation]
);
// Calculate map bounds to fit all markers with appropriate zoom levels