This commit is contained in:
lendry
2026-07-10 10:37:22 +03:00
parent caf12e64f7
commit a4b4577c55
13 changed files with 669 additions and 48 deletions

View File

@@ -0,0 +1,103 @@
'use client';
import { useEffect, useRef } from 'react';
import L from 'leaflet';
import { ExternalLink, MapPin } from 'lucide-react';
import { cn } from '@/lib/utils';
let iconsFixed = false;
function fixLeafletIcons() {
if (iconsFixed || typeof window === 'undefined') return;
iconsFixed = true;
L.Icon.Default.mergeOptions({
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
}
function buildMapsUrl(latitude: number, longitude: number) {
return `https://yandex.ru/maps/?pt=${longitude},${latitude}&z=16&l=map`;
}
export function ChatLocationCard({
latitude,
longitude,
address,
label,
variant = 'theirs'
}: {
latitude: number;
longitude: number;
address?: string;
label?: string;
variant?: 'mine' | 'theirs';
}) {
const mapRef = useRef<HTMLDivElement>(null);
const mapInstance = useRef<L.Map | null>(null);
const title = label?.trim() || address?.trim() || `${latitude.toFixed(5)}, ${longitude.toFixed(5)}`;
const mapsUrl = buildMapsUrl(latitude, longitude);
useEffect(() => {
fixLeafletIcons();
if (!mapRef.current || mapInstance.current) return;
const map = L.map(mapRef.current, {
zoomControl: false,
attributionControl: true,
dragging: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
keyboard: false,
touchZoom: false
}).setView([latitude, longitude], 15);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap'
}).addTo(map);
L.marker([latitude, longitude]).addTo(map);
mapInstance.current = map;
const resizeObserver = new ResizeObserver(() => {
map.invalidateSize();
});
resizeObserver.observe(mapRef.current);
return () => {
resizeObserver.disconnect();
map.remove();
mapInstance.current = null;
};
}, [latitude, longitude]);
return (
<a
href={mapsUrl}
target="_blank"
rel="noopener noreferrer"
data-chat-interactive="true"
className={cn(
'block w-[min(100%,280px)] overflow-hidden rounded-2xl border transition hover:shadow-md',
variant === 'mine' ? 'border-[#7ea06a]/35 bg-white' : 'border-[#dce3ec] bg-white'
)}
>
<div ref={mapRef} className="h-[160px] w-full" />
<div className="flex items-start gap-2 px-3 py-2.5">
<MapPin className={cn('mt-0.5 h-4 w-4 shrink-0', variant === 'mine' ? 'text-[#7ea06a]' : 'text-[#3390ec]')} />
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-[#1f2430]">{title}</p>
{address && label ? <p className="truncate text-xs text-[#667085]">{address}</p> : null}
</div>
<ExternalLink className="mt-0.5 h-4 w-4 shrink-0 text-[#667085]" />
</div>
</a>
);
}