Improve selection panel and hub card compass
All checks were successful
Build Docker Image / build (push) Successful in 4m44s

This commit is contained in:
Ruslan Bakiev
2026-02-06 15:21:24 +07:00
parent fa0465fabb
commit f0c687c3ff
4 changed files with 121 additions and 35 deletions

View File

@@ -16,16 +16,29 @@
]"
>
<div class="flex flex-col gap-1">
<!-- Title -->
<Text size="base" weight="semibold" class="truncate">{{ hub.name }}</Text>
<!-- Country left, distance right -->
<!-- Title + distance/compass -->
<div class="flex items-start justify-between gap-2">
<Text size="base" weight="semibold" class="truncate">{{ hub.name }}</Text>
<div class="flex items-center gap-2 text-xs text-white/70 whitespace-nowrap">
<Text v-if="distanceLabel" size="xs" class="text-white/70">{{ distanceLabel }}</Text>
<div v-if="bearing !== null" class="flex items-center gap-1">
<div class="w-6 h-6 rounded-full border border-white/20 bg-white/5 flex items-center justify-center">
<Icon
name="lucide:arrow-up"
size="12"
class="text-white/80"
:style="{ transform: `rotate(${bearing}deg)` }"
/>
</div>
<Text size="xs" class="text-white/60">{{ Math.round(bearing) }}°</Text>
</div>
</div>
</div>
<!-- Country -->
<div class="flex items-center justify-between">
<Text tone="muted" size="sm">
{{ countryFlag }} {{ hub.country || t('catalogMap.labels.country_unknown') }}
</Text>
<span v-if="distanceLabel" class="badge badge-neutral badge-dash text-xs">
{{ distanceLabel }}
</span>
</div>
<!-- Transport icons bottom -->
<div v-if="hub.transportTypes?.length" class="flex items-center gap-1 pt-1">
@@ -47,6 +60,8 @@ interface Hub {
name?: string | null
country?: string | null
countryCode?: string | null
latitude?: number | null
longitude?: number | null
distance?: string
distanceKm?: number | null
transportTypes?: (string | null)[] | null
@@ -54,6 +69,7 @@ interface Hub {
const props = defineProps<{
hub: Hub
origin?: { latitude: number; longitude: number } | null
selectable?: boolean
isSelected?: boolean
linkTo?: string
@@ -88,4 +104,27 @@ const distanceLabel = computed(() => {
if (props.hub.distanceKm != null) return `${Math.round(props.hub.distanceKm)} km`
return ''
})
const toRadians = (deg: number) => (deg * Math.PI) / 180
const toDegrees = (rad: number) => (rad * 180) / Math.PI
const bearing = computed(() => {
const origin = props.origin
const lat2 = props.hub.latitude
const lon2 = props.hub.longitude
if (!origin || lat2 == null || lon2 == null) return null
const lat1 = origin.latitude
const lon1 = origin.longitude
if (lat1 == null || lon1 == null) return null
const φ1 = toRadians(lat1)
const φ2 = toRadians(lat2)
const Δλ = toRadians(lon2 - lon1)
const y = Math.sin(Δλ) * Math.cos(φ2)
const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ)
const θ = Math.atan2(y, x)
const deg = (toDegrees(θ) + 360) % 360
return deg
})
</script>