feat(map): focus selected marker at max zoom and tighten collapsed device panel
All checks were successful
Deploy to Production / deploy (push) Successful in 1m51s
All checks were successful
Deploy to Production / deploy (push) Successful in 1m51s
This commit is contained in:
@@ -67,6 +67,10 @@ type TelemetrySnapshot = {
|
||||
|
||||
type ConnectionState = "connecting" | "live" | "retrying"
|
||||
type BaseMapType = "osm" | "amap" | "amap-satellite"
|
||||
type MapControlBridge = {
|
||||
__mapFitAll?: () => void
|
||||
__mapFocusDevice?: (coordinate: [number, number]) => void
|
||||
}
|
||||
|
||||
/* ---------- constants ---------- */
|
||||
|
||||
@@ -338,17 +342,38 @@ function FitBoundsControl({
|
||||
coordinates: Array<[number, number]>
|
||||
}) {
|
||||
const map = useMap()
|
||||
const fitAll = () => {
|
||||
const fitAll = useCallback(() => {
|
||||
if (!coordinates.length) return
|
||||
if (coordinates.length === 1) {
|
||||
map.setView(coordinates[0], 14, { animate: true })
|
||||
} else {
|
||||
map.fitBounds(coordinates, { padding: [40, 40] })
|
||||
}
|
||||
}
|
||||
// expose via a ref so the outside button can call it
|
||||
const ref = window as unknown as Record<string, () => void>
|
||||
ref.__mapFitAll = fitAll
|
||||
}, [coordinates, map])
|
||||
|
||||
const focusDevice = useCallback(
|
||||
(coordinate: [number, number]) => {
|
||||
const maxZoom = map.getMaxZoom()
|
||||
const targetZoom = Number.isFinite(maxZoom) ? maxZoom : 18
|
||||
map.setView(coordinate, targetZoom, { animate: true })
|
||||
},
|
||||
[map],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const ref = window as unknown as MapControlBridge
|
||||
ref.__mapFitAll = fitAll
|
||||
ref.__mapFocusDevice = focusDevice
|
||||
return () => {
|
||||
if (ref.__mapFitAll === fitAll) {
|
||||
delete ref.__mapFitAll
|
||||
}
|
||||
if (ref.__mapFocusDevice === focusDevice) {
|
||||
delete ref.__mapFocusDevice
|
||||
}
|
||||
}
|
||||
}, [fitAll, focusDevice])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -490,9 +515,14 @@ function Dashboard() {
|
||||
[devicesWithCoords, isGcj02],
|
||||
)
|
||||
|
||||
const handleDeviceClick = useCallback((deviceId: string) => {
|
||||
setActiveDeviceId(deviceId)
|
||||
}, [])
|
||||
const handleDeviceClick = useCallback(
|
||||
(deviceId: string, coordinate: [number, number]) => {
|
||||
setActiveDeviceId(deviceId)
|
||||
const mapBridge = window as unknown as MapControlBridge
|
||||
mapBridge.__mapFocusDevice?.(coordinate)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const isLive = status === "live"
|
||||
|
||||
@@ -574,11 +604,12 @@ function Dashboard() {
|
||||
{devicesWithCoords.map((device) => {
|
||||
const isOffline = checkIsOffline(device.last_event_time)
|
||||
const isSelected = activeDeviceId === device.device_id
|
||||
const [projLat, projLon] = projectCoord(
|
||||
const projectedCoordinate = projectCoord(
|
||||
device.lat as number,
|
||||
device.lon as number,
|
||||
isGcj02,
|
||||
)
|
||||
const [projLat, projLon] = projectedCoordinate
|
||||
const color = deviceMarkerColor(device.fix_status, isOffline)
|
||||
const icon = buildDeviceIcon(color, isSelected, isOffline)
|
||||
|
||||
@@ -587,7 +618,8 @@ function Dashboard() {
|
||||
position={[projLat, projLon]}
|
||||
icon={icon}
|
||||
eventHandlers={{
|
||||
click: () => handleDeviceClick(device.device_id),
|
||||
click: () =>
|
||||
handleDeviceClick(device.device_id, projectedCoordinate),
|
||||
}}
|
||||
key={device.device_id}
|
||||
>
|
||||
@@ -639,12 +671,14 @@ function Dashboard() {
|
||||
<div
|
||||
className={`absolute bottom-6 right-6 z-9999 w-80 max-w-[calc(100%-3rem)] transition-all duration-300 ${isPanelExpanded ? "opacity-80" : "opacity-100"}`}
|
||||
>
|
||||
<Card className="shadow-lg border bg-background/50 backdrop-blur-md overflow-hidden">
|
||||
<Card className="shadow-lg border bg-background/50 backdrop-blur-md overflow-hidden py-0 gap-0">
|
||||
<CardHeader
|
||||
className="p-3 flex flex-row items-center justify-between space-y-0 cursor-pointer select-none"
|
||||
className={`flex flex-row items-center justify-between space-y-0 cursor-pointer select-none transition-all duration-300 ${
|
||||
isPanelExpanded ? "p-3 min-h-11" : "px-3 py-0 min-h-0"
|
||||
}`}
|
||||
onClick={() => setIsPanelExpanded(!isPanelExpanded)}
|
||||
>
|
||||
<CardTitle className="text-sm font-semibold flex items-center gap-2">
|
||||
<CardTitle className="font-semibold flex items-center gap-2 text-sm">
|
||||
Selected Device
|
||||
{activeDevice?.signal_health &&
|
||||
!isPanelExpanded &&
|
||||
|
||||
Reference in New Issue
Block a user