Updated adsb + ais popups to include images
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
const BASE_SPEED_UNITS = {
|
||||
knots: { label: 'KTS', convert: (v: number) => Math.round(v) },
|
||||
@@ -48,6 +48,23 @@ const climbVal = computed(() => { const v = vertical.value.convert(props.plan
|
||||
const description = computed(() => props.plane.desc || [props.plane.manufacturer, props.plane.model].filter(Boolean).join(' ') || '')
|
||||
const operator = computed(() => props.plane.operator || props.plane.owner || '')
|
||||
|
||||
// ── Aircraft photo ────────────────────────────────────────────────────────────
|
||||
const photoUrl = ref<string | null>(null)
|
||||
const photoLoading = ref(false)
|
||||
|
||||
async function fetchPhoto(icao: string) {
|
||||
photoUrl.value = null
|
||||
if (!icao) return
|
||||
photoLoading.value = true
|
||||
const res = await fetch(`https://api.planespotters.net/pub/photos/hex/${icao.toLowerCase()}`)
|
||||
const data = await res.json()
|
||||
const photo = data?.photos?.[0]
|
||||
if (photo) photoUrl.value = photo.thumbnail_large?.src ?? photo.thumbnail?.src ?? null
|
||||
photoLoading.value = false
|
||||
}
|
||||
|
||||
watch(() => props.plane.icao || props.plane.hex, (icao) => fetchPhoto(icao), { immediate: true })
|
||||
|
||||
// ── Drag ──────────────────────────────────────────────────────────────────────
|
||||
const pos = ref({ ...props.position })
|
||||
const isDragging = ref(false)
|
||||
@@ -203,7 +220,7 @@ const navball = computed(() => {
|
||||
<button class="ap-close" @click.stop="emit('close')">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<!-- Meta -->
|
||||
<div class="ap-meta flex-r justify-between">
|
||||
<div class="flex-c">
|
||||
<span>{{ operator || 'Unknown Owner' }}</span>
|
||||
@@ -214,6 +231,13 @@ const navball = computed(() => {
|
||||
<span style="text-transform: capitalize">{{ plane.class || 'Unknown' }} • {{ plane.type || 'Unknown' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Photo -->
|
||||
<div v-if="photoUrl" class="ap-photo-wrap">
|
||||
<img class="ap-photo" :src="photoUrl" :alt="callsign" />
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="ap-body">
|
||||
<div class="ap-gauges">
|
||||
<div class="ap-gauge-wrap">
|
||||
@@ -309,6 +333,24 @@ const navball = computed(() => {
|
||||
}
|
||||
.ap-close:hover { background: rgba(255,255,255,0.35); }
|
||||
|
||||
/* ── Photo ── */
|
||||
.ap-photo-wrap {
|
||||
width: 100%;
|
||||
height: 160px;
|
||||
background: #111;
|
||||
border-bottom: 1px solid rgba(200,200,220,0.15);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.ap-photo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ap-body {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
Reference in New Issue
Block a user