fix
This commit is contained in:
103
apps/frontend/components/chat/chat-location-card.tsx
Normal file
103
apps/frontend/components/chat/chat-location-card.tsx
Normal 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: '© 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user